/** * Return a {@code RequestPredicate} that matches if request's HTTP method is {@code GET} * and the given {@code pattern} matches against the request path. * @param pattern the path pattern to match against * @return a predicate that matches if the request method is GET and if the given pattern * matches against the request path */ public static RequestPredicate GET(String pattern) { return method(HttpMethod.GET).and(path(pattern)); }
@Test public void and() { RequestPredicate predicate1 = request -> true; RequestPredicate predicate2 = request -> true; RequestPredicate predicate3 = request -> false; MockServerRequest request = MockServerRequest.builder().build(); assertTrue(predicate1.and(predicate2).test(request)); assertTrue(predicate2.and(predicate1).test(request)); assertFalse(predicate1.and(predicate3).test(request)); }
@Override public void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction) { indent(); predicate.accept(this); this.builder.append(" -> "); this.builder.append(handlerFunction).append('\n'); }
@Test public void or() { RequestPredicate predicate1 = request -> true; RequestPredicate predicate2 = request -> false; RequestPredicate predicate3 = request -> false; MockServerRequest request = MockServerRequest.builder().build(); assertTrue(predicate1.or(predicate2).test(request)); assertTrue(predicate2.or(predicate1).test(request)); assertFalse(predicate2.or(predicate3).test(request)); }
@Test public void negate() { RequestPredicate predicate = request -> false; RequestPredicate negated = predicate.negate(); MockServerRequest mockRequest = MockServerRequest.builder().build(); assertTrue(negated.test(mockRequest)); predicate = request -> true; negated = predicate.negate(); assertFalse(negated.test(mockRequest)); }
@Test public void predicates() { testPredicate(methods(HttpMethod.GET), "GET"); testPredicate(methods(HttpMethod.GET, HttpMethod.POST), "[GET, POST]"); testPredicate(path("/foo"), "/foo"); testPredicate(pathExtension("foo"), "*.foo"); testPredicate(contentType(MediaType.APPLICATION_JSON), "Content-Type: application/json"); testPredicate(contentType(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN), "Content-Type: [application/json, text/plain]"); testPredicate(accept(MediaType.APPLICATION_JSON), "Accept: application/json"); testPredicate(queryParam("foo", "bar"), "?foo == bar"); testPredicate(method(HttpMethod.GET).and(path("/foo")), "(GET && /foo)"); testPredicate(method(HttpMethod.GET).or(path("/foo")), "(GET || /foo)"); testPredicate(method(HttpMethod.GET).negate(), "!(GET)"); testPredicate(GET("/foo") .or(contentType(MediaType.TEXT_PLAIN)) .and(accept(MediaType.APPLICATION_JSON).negate()), "(((GET && /foo) || Content-Type: text/plain) && !(Accept: application/json))"); }
/** * Route to the given router function if the given request predicate applies. * @param predicate the predicate to test * @param routerFunction the router function to route to * @param <T> the type of the handler function * @return a router function that routes to {@code routerFunction} if * {@code predicate} evaluates to {@code true} * @see RequestPredicates */ public static <T extends ServerResponse> RouterFunction<T> subroute(RequestPredicate predicate, RouterFunction<T> routerFunction) { Assert.notNull(predicate, "'predicate' must not be null"); Assert.notNull(routerFunction, "'routerFunction' must not be null"); return request -> { if (predicate.test(request)) { ServerRequest subRequest = predicate.subRequest(request); return routerFunction.route(subRequest); } else { return Mono.empty(); } }; }
/** * Transform the given request into a request used for a nested route. For instance, * a path-based predicate can return a {@code ServerRequest} with a the path remaining * after a match. * <p>The default implementation returns an {@code Optional} wrapping the given path if * {@link #test(ServerRequest)} evaluates to {@code true}; or {@link Optional#empty()} * if it evaluates to {@code false}. * @param request the request to be nested * @return the nested request * @see RouterFunctions#nest(RequestPredicate, RouterFunction) */ default Optional<ServerRequest> nest(ServerRequest request) { return (test(request) ? Optional.of(request) : Optional.empty()); }
@Override public Optional<ServerRequest> nest(ServerRequest request) { return this.left.nest(request).flatMap(this.right::nest); }
/** * Return a {@code RequestPredicate} that matches if request's HTTP method is {@code HEAD} * and the given {@code pattern} matches against the request path. * @param pattern the path pattern to match against * @return a predicate that matches if the request method is HEAD and if the given pattern * matches against the request path */ public static RequestPredicate HEAD(String pattern) { return method(HttpMethod.HEAD).and(path(pattern)); }
@Override public boolean test(ServerRequest request) { Map<String, Object> oldAttributes = new HashMap<>(request.attributes()); if (this.left.test(request)) { return true; } else { restoreAttributes(request, oldAttributes); if (this.right.test(request)) { return true; } } restoreAttributes(request, oldAttributes); return false; }
@Override public void startNested(RequestPredicate predicate) { indent(); predicate.accept(this); this.builder.append(" => {\n"); this.indent++; }
@Override public Mono<HandlerFunction<T>> route(ServerRequest serverRequest) { return this.predicate.nest(serverRequest) .map(nestedRequest -> { if (logger.isTraceEnabled()) { String logPrefix = serverRequest.exchange().getLogPrefix(); logger.trace(logPrefix + String.format("Matched nested %s", this.predicate)); } return this.routerFunction.route(nestedRequest) .doOnNext(match -> { if (nestedRequest != serverRequest) { serverRequest.attributes().clear(); serverRequest.attributes() .putAll(nestedRequest.attributes()); } }); } ).orElseGet(Mono::empty); }
/** * Return a {@code RequestPredicate} that matches if request's HTTP method is {@code OPTIONS} * and the given {@code pattern} matches against the request path. * @param pattern the path pattern to match against * @return a predicate that matches if the request method is OPTIONS and if the given pattern * matches against the request path */ public static RequestPredicate OPTIONS(String pattern) { return method(HttpMethod.OPTIONS).and(path(pattern)); }
@Override public boolean test(ServerRequest request) { Map<String, Object> oldAttributes = new HashMap<>(request.attributes()); boolean result = !this.delegate.test(request); if (!result) { restoreAttributes(request, oldAttributes); } return result; }
@Test public void nestNoMatch() { HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build(); RouterFunction<ServerResponse> routerFunction = request -> Mono.just(handlerFunction); MockServerRequest request = MockServerRequest.builder().build(); RequestPredicate requestPredicate = mock(RequestPredicate.class); when(requestPredicate.nest(request)).thenReturn(Optional.empty()); RouterFunction<ServerResponse> result = RouterFunctions.nest(requestPredicate, routerFunction); assertNotNull(result); Mono<HandlerFunction<ServerResponse>> resultHandlerFunction = result.route(request); StepVerifier.create(resultHandlerFunction) .expectComplete() .verify(); }
/** * Return a {@code RequestPredicate} that matches if request's HTTP method is {@code DELETE} * and the given {@code pattern} matches against the request path. * @param pattern the path pattern to match against * @return a predicate that matches if the request method is DELETE and if the given pattern * matches against the request path */ public static RequestPredicate DELETE(String pattern) { return method(HttpMethod.DELETE).and(path(pattern)); }