/** * <strong>Central entry point to Spring's functional web framework.</strong> Exposes routing * functionality, such as to {@linkplain #route(RequestPredicate, HandlerFunction) create} a {@code * RouterFunction} given a {@code RequestPredicate} and {@code HandlerFunction}, and to do further * {@linkplain #subroute(RequestPredicate, RouterFunction) subrouting} on an existing routing * function. * * <p>Additionally, this class can {@linkplain #toHttpHandler(RouterFunction) transform} a {@code * RouterFunction} into an {@code HttpHandler}, which can be run in Servlet 3.1+, Reactor, RxNetty, * or Undertow. And it can {@linkplain #toHandlerMapping(RouterFunction, HandlerStrategies) * transform} a {@code RouterFunction} into an {@code HandlerMapping}, which can be run in a {@code * DispatcherHandler}. * * @author Arjen Poutsma * @since 5.0 */ public abstract class RouterFunctions { /** Name of the {@link ServerWebExchange} attribute that contains the {@link ServerRequest}. */ public static final String REQUEST_ATTRIBUTE = RouterFunctions.class.getName() + ".request"; /** * Name of the {@link ServerWebExchange} attribute that contains the URI templates map, mapping * variable names to values. */ public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE = RouterFunctions.class.getName() + ".uriTemplateVariables"; private static final HandlerFunction<Void> NOT_FOUND_HANDLER = request -> ServerResponse.notFound().build(); /** * Route to the given handler function if the given request predicate applies. * * @param predicate the predicate to test * @param handlerFunction the handler function to route to * @param <T> the type of the handler function * @return a routing function that routes to {@code handlerFunction} if {@code predicate} * evaluates to {@code true} * @see RequestPredicates */ public static <T> RouterFunction<T> route( RequestPredicate predicate, HandlerFunction<T> handlerFunction) { Assert.notNull(predicate, "'predicate' must not be null"); Assert.notNull(handlerFunction, "'handlerFunction' must not be null"); return request -> predicate.test(request) ? Optional.of(handlerFunction) : Optional.empty(); } /** * Route to the given routing function if the given request predicate applies. * * @param predicate the predicate to test * @param routerFunction the routing function to route to * @param <T> the type of the handler function * @return a routing function that routes to {@code routerFunction} if {@code predicate} evaluates * to {@code true} * @see RequestPredicates */ public static <T> 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 Optional.empty(); } }; } /** * Convert the given {@linkplain RouterFunction routing function} into a {@link HttpHandler}. This * conversion uses {@linkplain HandlerStrategies#builder() default strategies}. * * <p>The returned {@code HttpHandler} can be adapted to run in * * <ul> * <li>Servlet 3.1+ using the {@link * org.springframework.http.server.reactive.ServletHttpHandlerAdapter}, * <li>Reactor using the {@link * org.springframework.http.server.reactive.ReactorHttpHandlerAdapter}, * <li>RxNetty using the {@link * org.springframework.http.server.reactive.RxNettyHttpHandlerAdapter}, or * <li>Undertow using the {@link * org.springframework.http.server.reactive.UndertowHttpHandlerAdapter}. * </ul> * * @param routerFunction the routing function to convert * @return an http handler that handles HTTP request using the given routing function */ public static HttpHandler toHttpHandler(RouterFunction<?> routerFunction) { return toHttpHandler(routerFunction, HandlerStrategies.withDefaults()); } /** * Convert the given {@linkplain RouterFunction routing function} into a {@link HttpHandler}, * using the given strategies. * * <p>The returned {@code HttpHandler} can be adapted to run in * * <ul> * <li>Servlet 3.1+ using the {@link * org.springframework.http.server.reactive.ServletHttpHandlerAdapter}, * <li>Reactor using the {@link * org.springframework.http.server.reactive.ReactorHttpHandlerAdapter}, * <li>RxNetty using the {@link * org.springframework.http.server.reactive.RxNettyHttpHandlerAdapter}, or * <li>Undertow using the {@link * org.springframework.http.server.reactive.UndertowHttpHandlerAdapter}. * </ul> * * @param routerFunction the routing function to convert * @param strategies the strategies to use * @return an http handler that handles HTTP request using the given routing function */ public static HttpHandler toHttpHandler( RouterFunction<?> routerFunction, HandlerStrategies strategies) { Assert.notNull(routerFunction, "RouterFunction must not be null"); Assert.notNull(strategies, "HandlerStrategies must not be null"); return new HttpWebHandlerAdapter( exchange -> { ServerRequest request = new DefaultServerRequest(exchange, strategies); addAttributes(exchange, request); HandlerFunction<?> handlerFunction = routerFunction.route(request).orElse(notFound()); ServerResponse<?> response = handlerFunction.handle(request); return response.writeTo(exchange, strategies); }); } /** * Convert the given {@code RouterFunction} into a {@code HandlerMapping}. This conversion uses * {@linkplain HandlerStrategies#builder() default strategies}. * * <p>The returned {@code HandlerMapping} can be run in a {@link * org.springframework.web.reactive.DispatcherHandler}. * * @param routerFunction the routing function to convert * @return an handler mapping that maps HTTP request to a handler using the given routing function * @see org.springframework.web.reactive.function.support.HandlerFunctionAdapter * @see org.springframework.web.reactive.function.support.ServerResponseResultHandler */ public static HandlerMapping toHandlerMapping(RouterFunction<?> routerFunction) { return toHandlerMapping(routerFunction, HandlerStrategies.withDefaults()); } /** * Convert the given {@linkplain RouterFunction routing function} into a {@link HandlerMapping}, * using the given strategies. * * <p>The returned {@code HandlerMapping} can be run in a {@link * org.springframework.web.reactive.DispatcherHandler}. * * @param routerFunction the routing function to convert * @param strategies the strategies to use * @return an handler mapping that maps HTTP request to a handler using the given routing function * @see org.springframework.web.reactive.function.support.HandlerFunctionAdapter * @see org.springframework.web.reactive.function.support.ServerResponseResultHandler */ public static HandlerMapping toHandlerMapping( RouterFunction<?> routerFunction, HandlerStrategies strategies) { Assert.notNull(routerFunction, "RouterFunction must not be null"); Assert.notNull(strategies, "HandlerStrategies must not be null"); return exchange -> { ServerRequest request = new DefaultServerRequest(exchange, strategies); addAttributes(exchange, request); Optional<? extends HandlerFunction<?>> route = routerFunction.route(request); return Mono.justOrEmpty(route); }; } private static void addAttributes(ServerWebExchange exchange, ServerRequest request) { Map<String, Object> attributes = exchange.getAttributes(); attributes.put(REQUEST_ATTRIBUTE, request); } @SuppressWarnings("unchecked") private static <T> HandlerFunction<T> notFound() { return (HandlerFunction<T>) NOT_FOUND_HANDLER; } @SuppressWarnings("unchecked") static <T> HandlerFunction<T> cast(HandlerFunction<?> handlerFunction) { return (HandlerFunction<T>) handlerFunction; } }