001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.function; 018 019import java.util.Optional; 020import java.util.function.BiFunction; 021import java.util.function.Consumer; 022import java.util.function.Function; 023import java.util.function.Predicate; 024import java.util.function.Supplier; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028 029import org.springframework.core.io.Resource; 030import org.springframework.util.Assert; 031 032/** 033 * <strong>Central entry point to Spring's functional web framework.</strong> 034 * Exposes routing functionality, such as to {@linkplain #route() create} a 035 * {@code RouterFunction} using a discoverable builder-style API, to 036 * {@linkplain #route(RequestPredicate, HandlerFunction) create} a {@code RouterFunction} 037 * given a {@code RequestPredicate} and {@code HandlerFunction}, and to do further 038 * {@linkplain #nest(RequestPredicate, RouterFunction) subrouting} on an existing routing 039 * function. 040 * 041 * @author Arjen Poutsma 042 * @since 5.2 043 */ 044public abstract class RouterFunctions { 045 046 private static final Log logger = LogFactory.getLog(RouterFunctions.class); 047 048 /** 049 * Name of the request attribute that contains the {@link ServerRequest}. 050 */ 051 public static final String REQUEST_ATTRIBUTE = RouterFunctions.class.getName() + ".request"; 052 053 /** 054 * Name of the request attribute that contains the URI 055 * templates map, mapping variable names to values. 056 */ 057 public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE = 058 RouterFunctions.class.getName() + ".uriTemplateVariables"; 059 060 /** 061 * Name of the request attribute that contains the matching pattern, as a 062 * {@link org.springframework.web.util.pattern.PathPattern}. 063 */ 064 public static final String MATCHING_PATTERN_ATTRIBUTE = 065 RouterFunctions.class.getName() + ".matchingPattern"; 066 067 068 /** 069 * Offers a discoverable way to create router functions through a builder-style interface. 070 * @return a router function builder 071 */ 072 public static Builder route() { 073 return new RouterFunctionBuilder(); 074 } 075 076 /** 077 * Route to the given handler function if the given request predicate applies. 078 * <p>For instance, the following example routes GET requests for "/user" to the 079 * {@code listUsers} method in {@code userController}: 080 * <pre class="code"> 081 * RouterFunction<ServerResponse> route = 082 * RouterFunctions.route(RequestPredicates.GET("/user"), userController::listUsers); 083 * </pre> 084 * @param predicate the predicate to test 085 * @param handlerFunction the handler function to route to if the predicate applies 086 * @param <T> the type of response returned by the handler function 087 * @return a router function that routes to {@code handlerFunction} if 088 * {@code predicate} evaluates to {@code true} 089 * @see RequestPredicates 090 */ 091 public static <T extends ServerResponse> RouterFunction<T> route( 092 RequestPredicate predicate, HandlerFunction<T> handlerFunction) { 093 094 return new DefaultRouterFunction<>(predicate, handlerFunction); 095 } 096 097 /** 098 * Route to the given router function if the given request predicate applies. This method can be 099 * used to create <strong>nested routes</strong>, where a group of routes share a common path 100 * (prefix), header, or other request predicate. 101 * <p>For instance, the following example first creates a composed route that resolves to 102 * {@code listUsers} for a GET, and {@code createUser} for a POST. This composed route then gets 103 * nested with a "/user" path predicate, so that GET requests for "/user" will list users, 104 * and POST request for "/user" will create a new user. 105 * <pre class="code"> 106 * RouterFunction<ServerResponse> userRoutes = 107 * RouterFunctions.route(RequestPredicates.method(HttpMethod.GET), this::listUsers) 108 * .andRoute(RequestPredicates.method(HttpMethod.POST), this::createUser); 109 * RouterFunction<ServerResponse> nestedRoute = 110 * RouterFunctions.nest(RequestPredicates.path("/user"), userRoutes); 111 * </pre> 112 * @param predicate the predicate to test 113 * @param routerFunction the nested router function to delegate to if the predicate applies 114 * @param <T> the type of response returned by the handler function 115 * @return a router function that routes to {@code routerFunction} if 116 * {@code predicate} evaluates to {@code true} 117 * @see RequestPredicates 118 */ 119 public static <T extends ServerResponse> RouterFunction<T> nest( 120 RequestPredicate predicate, RouterFunction<T> routerFunction) { 121 122 return new DefaultNestedRouterFunction<>(predicate, routerFunction); 123 } 124 125 /** 126 * Route requests that match the given pattern to resources relative to the given root location. 127 * For instance 128 * <pre class="code"> 129 * Resource location = new FileSystemResource("public-resources/"); 130 * RouterFunction<ServerResponse> resources = RouterFunctions.resources("/resources/**", location); 131 * </pre> 132 * @param pattern the pattern to match 133 * @param location the location directory relative to which resources should be resolved 134 * @return a router function that routes to resources 135 * @see #resourceLookupFunction(String, Resource) 136 */ 137 public static RouterFunction<ServerResponse> resources(String pattern, Resource location) { 138 return resources(resourceLookupFunction(pattern, location)); 139 } 140 141 /** 142 * Returns the resource lookup function used by {@link #resources(String, Resource)}. 143 * The returned function can be {@linkplain Function#andThen(Function) composed} on, for 144 * instance to return a default resource when the lookup function does not match: 145 * <pre class="code"> 146 * Optional<Resource> defaultResource = Optional.of(new ClassPathResource("index.html")); 147 * Function<ServerRequest, Optional<Resource>> lookupFunction = 148 * RouterFunctions.resourceLookupFunction("/resources/**", new FileSystemResource("public-resources/")) 149 * .andThen(resource -> resource.or(() -> defaultResource)); 150 * RouterFunction<ServerResponse> resources = RouterFunctions.resources(lookupFunction); 151 * </pre> 152 * @param pattern the pattern to match 153 * @param location the location directory relative to which resources should be resolved 154 * @return the default resource lookup function for the given parameters. 155 */ 156 public static Function<ServerRequest, Optional<Resource>> resourceLookupFunction(String pattern, Resource location) { 157 return new PathResourceLookupFunction(pattern, location); 158 } 159 160 /** 161 * Route to resources using the provided lookup function. If the lookup function provides a 162 * {@link Resource} for the given request, it will be it will be exposed using a 163 * {@link HandlerFunction} that handles GET, HEAD, and OPTIONS requests. 164 * @param lookupFunction the function to provide a {@link Resource} given the {@link ServerRequest} 165 * @return a router function that routes to resources 166 */ 167 public static RouterFunction<ServerResponse> resources(Function<ServerRequest, Optional<Resource>> lookupFunction) { 168 return new ResourcesRouterFunction(lookupFunction); 169 } 170 171 172 /** 173 * Represents a discoverable builder for router functions. 174 * Obtained via {@link RouterFunctions#route()}. 175 */ 176 public interface Builder { 177 178 /** 179 * Adds a route to the given handler function that handles all HTTP {@code GET} requests 180 * that match the given pattern. 181 * @param pattern the pattern to match to 182 * @param handlerFunction the handler function to handle all {@code GET} requests that 183 * match {@code pattern} 184 * @return this builder 185 */ 186 Builder GET(String pattern, HandlerFunction<ServerResponse> handlerFunction); 187 188 /** 189 * Adds a route to the given handler function that handles all HTTP {@code GET} requests 190 * that match the given pattern and predicate. 191 * <p>For instance, the following example routes GET requests for "/user" that accept JSON 192 * to the {@code listUsers} method in {@code userController}: 193 * <pre class="code"> 194 * RouterFunction<ServerResponse> route = 195 * RouterFunctions.route() 196 * .GET("/user", RequestPredicates.accept(MediaType.APPLICATION_JSON), userController::listUsers) 197 * .build(); 198 * </pre> 199 * @param pattern the pattern to match to 200 * @param predicate additional predicate to match 201 * @param handlerFunction the handler function to handle all {@code GET} requests that 202 * match {@code pattern} 203 * @return this builder 204 * @see RequestPredicates 205 */ 206 Builder GET(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction); 207 208 /** 209 * Adds a route to the given handler function that handles all HTTP {@code HEAD} requests 210 * that match the given pattern. 211 * @param pattern the pattern to match to 212 * @param handlerFunction the handler function to handle all {@code HEAD} requests that 213 * match {@code pattern} 214 * @return this builder 215 */ 216 Builder HEAD(String pattern, HandlerFunction<ServerResponse> handlerFunction); 217 218 /** 219 * Adds a route to the given handler function that handles all HTTP {@code HEAD} requests 220 * that match the given pattern and predicate. 221 * @param pattern the pattern to match to 222 * @param predicate additional predicate to match 223 * @param handlerFunction the handler function to handle all {@code HEAD} requests that 224 * match {@code pattern} 225 * @return this builder 226 */ 227 Builder HEAD(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction); 228 229 /** 230 * Adds a route to the given handler function that handles all HTTP {@code POST} requests 231 * that match the given pattern. 232 * @param pattern the pattern to match to 233 * @param handlerFunction the handler function to handle all {@code POST} requests that 234 * match {@code pattern} 235 * @return this builder 236 */ 237 Builder POST(String pattern, HandlerFunction<ServerResponse> handlerFunction); 238 239 /** 240 * Adds a route to the given handler function that handles all HTTP {@code POST} requests 241 * that match the given pattern and predicate. 242 * <p>For instance, the following example routes POST requests for "/user" that contain JSON 243 * to the {@code addUser} method in {@code userController}: 244 * <pre class="code"> 245 * RouterFunction<ServerResponse> route = 246 * RouterFunctions.route() 247 * .POST("/user", RequestPredicates.contentType(MediaType.APPLICATION_JSON), userController::addUser) 248 * .build(); 249 * </pre> 250 * @param pattern the pattern to match to 251 * @param predicate additional predicate to match 252 * @param handlerFunction the handler function to handle all {@code POST} requests that 253 * match {@code pattern} 254 * @return this builder 255 */ 256 Builder POST(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction); 257 258 /** 259 * Adds a route to the given handler function that handles all HTTP {@code PUT} requests 260 * that match the given pattern. 261 * @param pattern the pattern to match to 262 * @param handlerFunction the handler function to handle all {@code PUT} requests that 263 * match {@code pattern} 264 * @return this builder 265 */ 266 Builder PUT(String pattern, HandlerFunction<ServerResponse> handlerFunction); 267 268 /** 269 * Adds a route to the given handler function that handles all HTTP {@code PUT} requests 270 * that match the given pattern and predicate. 271 * <p>For instance, the following example routes PUT requests for "/user" that contain JSON 272 * to the {@code editUser} method in {@code userController}: 273 * <pre class="code"> 274 * RouterFunction<ServerResponse> route = 275 * RouterFunctions.route() 276 * .PUT("/user", RequestPredicates.contentType(MediaType.APPLICATION_JSON), userController::editUser) 277 * .build(); 278 * </pre> 279 * @param pattern the pattern to match to 280 * @param predicate additional predicate to match 281 * @param handlerFunction the handler function to handle all {@code PUT} requests that 282 * match {@code pattern} 283 * @return this builder 284 */ 285 Builder PUT(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction); 286 287 /** 288 * Adds a route to the given handler function that handles all HTTP {@code PATCH} requests 289 * that match the given pattern. 290 * @param pattern the pattern to match to 291 * @param handlerFunction the handler function to handle all {@code PATCH} requests that 292 * match {@code pattern} 293 * @return this builder 294 */ 295 Builder PATCH(String pattern, HandlerFunction<ServerResponse> handlerFunction); 296 297 /** 298 * Adds a route to the given handler function that handles all HTTP {@code PATCH} requests 299 * that match the given pattern and predicate. 300 * <p>For instance, the following example routes PATCH requests for "/user" that contain JSON 301 * to the {@code editUser} method in {@code userController}: 302 * <pre class="code"> 303 * RouterFunction<ServerResponse> route = 304 * RouterFunctions.route() 305 * .PATCH("/user", RequestPredicates.contentType(MediaType.APPLICATION_JSON), userController::editUser) 306 * .build(); 307 * </pre> 308 * @param pattern the pattern to match to 309 * @param predicate additional predicate to match 310 * @param handlerFunction the handler function to handle all {@code PATCH} requests that 311 * match {@code pattern} 312 * @return this builder 313 */ 314 Builder PATCH(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction); 315 316 /** 317 * Adds a route to the given handler function that handles all HTTP {@code DELETE} requests 318 * that match the given pattern. 319 * @param pattern the pattern to match to 320 * @param handlerFunction the handler function to handle all {@code DELETE} requests that 321 * match {@code pattern} 322 * @return this builder 323 */ 324 Builder DELETE(String pattern, HandlerFunction<ServerResponse> handlerFunction); 325 326 /** 327 * Adds a route to the given handler function that handles all HTTP {@code DELETE} requests 328 * that match the given pattern and predicate. 329 * @param pattern the pattern to match to 330 * @param predicate additional predicate to match 331 * @param handlerFunction the handler function to handle all {@code DELETE} requests that 332 * match {@code pattern} 333 * @return this builder 334 */ 335 Builder DELETE(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction); 336 337 /** 338 * Adds a route to the given handler function that handles all HTTP {@code OPTIONS} requests 339 * that match the given pattern. 340 * @param pattern the pattern to match to 341 * @param handlerFunction the handler function to handle all {@code OPTIONS} requests that 342 * match {@code pattern} 343 * @return this builder 344 */ 345 Builder OPTIONS(String pattern, HandlerFunction<ServerResponse> handlerFunction); 346 347 /** 348 * Adds a route to the given handler function that handles all requests that match the 349 * given predicate. 350 * 351 * @param predicate the request predicate to match 352 * @param handlerFunction the handler function to handle all requests that match the predicate 353 * @return this builder 354 * @see RequestPredicates 355 */ 356 Builder route(RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction); 357 358 /** 359 * Adds a route to the given handler function that handles all HTTP {@code OPTIONS} requests 360 * that match the given pattern and predicate. 361 * @param pattern the pattern to match to 362 * @param predicate additional predicate to match 363 * @param handlerFunction the handler function to handle all {@code OPTIONS} requests that 364 * match {@code pattern} 365 * @return this builder 366 */ 367 Builder OPTIONS(String pattern, RequestPredicate predicate, HandlerFunction<ServerResponse> handlerFunction); 368 369 /** 370 * Adds the given route to this builder. Can be used to merge externally defined router 371 * functions into this builder, or can be combined with 372 * {@link RouterFunctions#route(RequestPredicate, HandlerFunction)} 373 * to allow for more flexible predicate matching. 374 * <p>For instance, the following example adds the router function returned from 375 * {@code OrderController.routerFunction()}. 376 * to the {@code changeUser} method in {@code userController}: 377 * <pre class="code"> 378 * RouterFunction<ServerResponse> route = 379 * RouterFunctions.route() 380 * .GET("/users", userController::listUsers) 381 * .add(orderController.routerFunction()); 382 * .build(); 383 * </pre> 384 * @param routerFunction the router function to be added 385 * @return this builder 386 * @see RequestPredicates 387 */ 388 Builder add(RouterFunction<ServerResponse> routerFunction); 389 390 /** 391 * Route requests that match the given pattern to resources relative to the given root location. 392 * For instance 393 * <pre class="code"> 394 * Resource location = new FileSystemResource("public-resources/"); 395 * RouterFunction<ServerResponse> resources = RouterFunctions.resources("/resources/**", location); 396 * </pre> 397 * @param pattern the pattern to match 398 * @param location the location directory relative to which resources should be resolved 399 * @return this builder 400 */ 401 Builder resources(String pattern, Resource location); 402 403 /** 404 * Route to resources using the provided lookup function. If the lookup function provides a 405 * {@link Resource} for the given request, it will be it will be exposed using a 406 * {@link HandlerFunction} that handles GET, HEAD, and OPTIONS requests. 407 * @param lookupFunction the function to provide a {@link Resource} given the {@link ServerRequest} 408 * @return this builder 409 */ 410 Builder resources(Function<ServerRequest, Optional<Resource>> lookupFunction); 411 412 /** 413 * Route to the supplied router function if the given request predicate applies. This method 414 * can be used to create <strong>nested routes</strong>, where a group of routes share a 415 * common path (prefix), header, or other request predicate. 416 * <p>For instance, the following example creates a nested route with a "/user" path 417 * predicate, so that GET requests for "/user" will list users, 418 * and POST request for "/user" will create a new user. 419 * <pre class="code"> 420 * RouterFunction<ServerResponse> nestedRoute = 421 * RouterFunctions.route() 422 * .nest(RequestPredicates.path("/user"), () -> 423 * RouterFunctions.route() 424 * .GET(this::listUsers) 425 * .POST(this::createUser) 426 * .build()) 427 * .build(); 428 * </pre> 429 * @param predicate the predicate to test 430 * @param routerFunctionSupplier supplier for the nested router function to delegate to if 431 * the predicate applies 432 * @return this builder 433 * @see RequestPredicates 434 */ 435 Builder nest(RequestPredicate predicate, Supplier<RouterFunction<ServerResponse>> routerFunctionSupplier); 436 437 /** 438 * Route to a built router function if the given request predicate applies. 439 * This method can be used to create <strong>nested routes</strong>, where a group of routes 440 * share a common path (prefix), header, or other request predicate. 441 * <p>For instance, the following example creates a nested route with a "/user" path 442 * predicate, so that GET requests for "/user" will list users, 443 * and POST request for "/user" will create a new user. 444 * <pre class="code"> 445 * RouterFunction<ServerResponse> nestedRoute = 446 * RouterFunctions.route() 447 * .nest(RequestPredicates.path("/user"), builder -> 448 * builder.GET(this::listUsers) 449 * .POST(this::createUser)) 450 * .build(); 451 * </pre> 452 * @param predicate the predicate to test 453 * @param builderConsumer consumer for a {@code Builder} that provides the nested router 454 * function 455 * @return this builder 456 * @see RequestPredicates 457 */ 458 Builder nest(RequestPredicate predicate, Consumer<Builder> builderConsumer); 459 460 /** 461 * Route to the supplied router function if the given path prefix pattern applies. This method 462 * can be used to create <strong>nested routes</strong>, where a group of routes share a 463 * common path prefix. Specifically, this method can be used to merge externally defined 464 * router functions under a path prefix. 465 * <p>For instance, the following example creates a nested route with a "/user" path 466 * predicate that delegates to the router function defined in {@code userController}, 467 * and with a "/order" path that delegates to {@code orderController}. 468 * <pre class="code"> 469 * RouterFunction<ServerResponse> nestedRoute = 470 * RouterFunctions.route() 471 * .path("/user", userController::routerFunction) 472 * .path("/order", orderController::routerFunction) 473 * .build(); 474 * </pre> 475 * @param pattern the pattern to match to 476 * @param routerFunctionSupplier supplier for the nested router function to delegate to if 477 * the pattern matches 478 * @return this builder 479 */ 480 Builder path(String pattern, Supplier<RouterFunction<ServerResponse>> routerFunctionSupplier); 481 482 /** 483 * Route to a built router function if the given path prefix pattern applies. 484 * This method can be used to create <strong>nested routes</strong>, where a group of routes 485 * share a common path prefix. 486 * <p>For instance, the following example creates a nested route with a "/user" path 487 * predicate, so that GET requests for "/user" will list users, 488 * and POST request for "/user" will create a new user. 489 * <pre class="code"> 490 * RouterFunction<ServerResponse> nestedRoute = 491 * RouterFunctions.route() 492 * .path("/user", builder -> 493 * builder.GET(this::listUsers) 494 * .POST(this::createUser)) 495 * .build(); 496 * </pre> 497 * @param pattern the pattern to match to 498 * @param builderConsumer consumer for a {@code Builder} that provides the nested router 499 * function 500 * @return this builder 501 */ 502 Builder path(String pattern, Consumer<Builder> builderConsumer); 503 504 /** 505 * Filters all routes created by this builder with the given filter function. Filter 506 * functions are typically used to address cross-cutting concerns, such as logging, 507 * security, etc. 508 * <p>For instance, the following example creates a filter that returns a 401 Unauthorized 509 * response if the request does not contain the necessary authentication headers. 510 * <pre class="code"> 511 * RouterFunction<ServerResponse> filteredRoute = 512 * RouterFunctions.route() 513 * .GET("/user", this::listUsers) 514 * .filter((request, next) -> { 515 * // check for authentication headers 516 * if (isAuthenticated(request)) { 517 * return next.handle(request); 518 * } 519 * else { 520 * return ServerResponse.status(HttpStatus.UNAUTHORIZED).build(); 521 * } 522 * }) 523 * .build(); 524 * </pre> 525 * @param filterFunction the function to filter all routes built by this builder 526 * @return this builder 527 */ 528 Builder filter(HandlerFilterFunction<ServerResponse, ServerResponse> filterFunction); 529 530 /** 531 * Filter the request object for all routes created by this builder with the given request 532 * processing function. Filters are typically used to address cross-cutting concerns, such 533 * as logging, security, etc. 534 * <p>For instance, the following example creates a filter that logs the request before 535 * the handler function executes. 536 * <pre class="code"> 537 * RouterFunction<ServerResponse> filteredRoute = 538 * RouterFunctions.route() 539 * .GET("/user", this::listUsers) 540 * .before(request -> { 541 * log(request); 542 * return request; 543 * }) 544 * .build(); 545 * </pre> 546 * @param requestProcessor a function that transforms the request 547 * @return this builder 548 */ 549 Builder before(Function<ServerRequest, ServerRequest> requestProcessor); 550 551 /** 552 * Filter the response object for all routes created by this builder with the given response 553 * processing function. Filters are typically used to address cross-cutting concerns, such 554 * as logging, security, etc. 555 * <p>For instance, the following example creates a filter that logs the response after 556 * the handler function executes. 557 * <pre class="code"> 558 * RouterFunction<ServerResponse> filteredRoute = 559 * RouterFunctions.route() 560 * .GET("/user", this::listUsers) 561 * .after((request, response) -> { 562 * log(response); 563 * return response; 564 * }) 565 * .build(); 566 * </pre> 567 * @param responseProcessor a function that transforms the response 568 * @return this builder 569 */ 570 Builder after(BiFunction<ServerRequest, ServerResponse, ServerResponse> responseProcessor); 571 572 /** 573 * Filters all exceptions that match the predicate by applying the given response provider 574 * function. 575 * <p>For instance, the following example creates a filter that returns a 500 response 576 * status when an {@code IllegalStateException} occurs. 577 * <pre class="code"> 578 * RouterFunction<ServerResponse> filteredRoute = 579 * RouterFunctions.route() 580 * .GET("/user", this::listUsers) 581 * .onError(e -> e instanceof IllegalStateException, 582 * (e, request) -> ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).build()) 583 * .build(); 584 * </pre> 585 * @param predicate the type of exception to filter 586 * @param responseProvider a function that creates a response 587 * @return this builder 588 */ 589 Builder onError(Predicate<Throwable> predicate, 590 BiFunction<Throwable, ServerRequest, ServerResponse> responseProvider); 591 592 /** 593 * Filters all exceptions of the given type by applying the given response provider 594 * function. 595 * <p>For instance, the following example creates a filter that returns a 500 response 596 * status when an {@code IllegalStateException} occurs. 597 * <pre class="code"> 598 * RouterFunction<ServerResponse> filteredRoute = 599 * RouterFunctions.route() 600 * .GET("/user", this::listUsers) 601 * .onError(IllegalStateException.class, 602 * (e, request) -> ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).build()) 603 * .build(); 604 * </pre> 605 * @param exceptionType the type of exception to filter 606 * @param responseProvider a function that creates a response 607 * @return this builder 608 */ 609 Builder onError(Class<? extends Throwable> exceptionType, 610 BiFunction<Throwable, ServerRequest, ServerResponse> responseProvider); 611 612 /** 613 * Builds the {@code RouterFunction}. All created routes are 614 * {@linkplain RouterFunction#and(RouterFunction) composed} with one another, and filters 615 * (if any) are applied to the result. 616 * @return the built router function 617 */ 618 RouterFunction<ServerResponse> build(); 619 } 620 /** 621 * Receives notifications from the logical structure of router functions. 622 */ 623 public interface Visitor { 624 625 /** 626 * Receive notification of the beginning of a nested router function. 627 * @param predicate the predicate that applies to the nested router functions 628 * @see RouterFunctions#nest(RequestPredicate, RouterFunction) 629 */ 630 void startNested(RequestPredicate predicate); 631 632 /** 633 * Receive notification of the end of a nested router function. 634 * @param predicate the predicate that applies to the nested router functions 635 * @see RouterFunctions#nest(RequestPredicate, RouterFunction) 636 */ 637 void endNested(RequestPredicate predicate); 638 639 /** 640 * Receive notification of a standard predicated route to a handler function. 641 * @param predicate the predicate that applies to the handler function 642 * @param handlerFunction the handler function. 643 * @see RouterFunctions#route(RequestPredicate, HandlerFunction) 644 */ 645 void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction); 646 647 /** 648 * Receive notification of a resource router function. 649 * @param lookupFunction the lookup function for the resources 650 * @see RouterFunctions#resources(Function) 651 */ 652 void resources(Function<ServerRequest, Optional<Resource>> lookupFunction); 653 654 /** 655 * Receive notification of an unknown router function. This method is called for router 656 * functions that were not created via the various {@link RouterFunctions} methods. 657 * @param routerFunction the router function 658 */ 659 void unknown(RouterFunction<?> routerFunction); 660 } 661 662 663 abstract static class AbstractRouterFunction<T extends ServerResponse> implements RouterFunction<T> { 664 665 @Override 666 public String toString() { 667 ToStringVisitor visitor = new ToStringVisitor(); 668 accept(visitor); 669 return visitor.toString(); 670 } 671 } 672 673 /** 674 * A composed routing function that first invokes one function, and then invokes the 675 * another function (of the same response type {@code T}) if this route had 676 * {@linkplain Optional#empty() no result}. 677 * @param <T> the server response type 678 */ 679 static final class SameComposedRouterFunction<T extends ServerResponse> extends AbstractRouterFunction<T> { 680 681 private final RouterFunction<T> first; 682 683 private final RouterFunction<T> second; 684 685 public SameComposedRouterFunction(RouterFunction<T> first, RouterFunction<T> second) { 686 this.first = first; 687 this.second = second; 688 } 689 690 @Override 691 public Optional<HandlerFunction<T>> route(ServerRequest request) { 692 Optional<HandlerFunction<T>> firstRoute = this.first.route(request); 693 if (firstRoute.isPresent()) { 694 return firstRoute; 695 } 696 else { 697 return this.second.route(request); 698 } 699 } 700 701 @Override 702 public void accept(Visitor visitor) { 703 this.first.accept(visitor); 704 this.second.accept(visitor); 705 } 706 } 707 708 /** 709 * A composed routing function that first invokes one function, and then invokes 710 * another function (of a different response type) if this route had 711 * {@linkplain Optional#empty() no result}. 712 */ 713 static final class DifferentComposedRouterFunction extends AbstractRouterFunction<ServerResponse> { 714 715 private final RouterFunction<?> first; 716 717 private final RouterFunction<?> second; 718 719 public DifferentComposedRouterFunction(RouterFunction<?> first, RouterFunction<?> second) { 720 this.first = first; 721 this.second = second; 722 } 723 724 @Override 725 @SuppressWarnings("unchecked") 726 public Optional<HandlerFunction<ServerResponse>> route(ServerRequest request) { 727 Optional<? extends HandlerFunction<?>> firstRoute = this.first.route(request); 728 if (firstRoute.isPresent()) { 729 return (Optional<HandlerFunction<ServerResponse>>) firstRoute; 730 } 731 else { 732 Optional<? extends HandlerFunction<?>> secondRoute = this.second.route(request); 733 return (Optional<HandlerFunction<ServerResponse>>) secondRoute; 734 } 735 } 736 737 @Override 738 public void accept(Visitor visitor) { 739 this.first.accept(visitor); 740 this.second.accept(visitor); 741 } 742 } 743 744 745 /** 746 * Filter the specified {@linkplain HandlerFunction handler functions} with the given 747 * {@linkplain HandlerFilterFunction filter function}. 748 * @param <T> the type of the {@linkplain HandlerFunction handler function} to filter 749 * @param <S> the type of the response of the function 750 */ 751 static final class FilteredRouterFunction<T extends ServerResponse, S extends ServerResponse> 752 implements RouterFunction<S> { 753 754 private final RouterFunction<T> routerFunction; 755 756 private final HandlerFilterFunction<T, S> filterFunction; 757 758 public FilteredRouterFunction( 759 RouterFunction<T> routerFunction, 760 HandlerFilterFunction<T, S> filterFunction) { 761 this.routerFunction = routerFunction; 762 this.filterFunction = filterFunction; 763 } 764 765 @Override 766 public Optional<HandlerFunction<S>> route(ServerRequest request) { 767 return this.routerFunction.route(request).map(this.filterFunction::apply); 768 } 769 770 @Override 771 public void accept(Visitor visitor) { 772 this.routerFunction.accept(visitor); 773 } 774 775 @Override 776 public String toString() { 777 return this.routerFunction.toString(); 778 } 779 } 780 781 private static final class DefaultRouterFunction<T extends ServerResponse> 782 extends AbstractRouterFunction<T> { 783 784 private final RequestPredicate predicate; 785 786 private final HandlerFunction<T> handlerFunction; 787 788 public DefaultRouterFunction(RequestPredicate predicate, HandlerFunction<T> handlerFunction) { 789 Assert.notNull(predicate, "Predicate must not be null"); 790 Assert.notNull(handlerFunction, "HandlerFunction must not be null"); 791 this.predicate = predicate; 792 this.handlerFunction = handlerFunction; 793 } 794 795 @Override 796 public Optional<HandlerFunction<T>> route(ServerRequest request) { 797 if (this.predicate.test(request)) { 798 if (logger.isTraceEnabled()) { 799 logger.trace(String.format("Predicate \"%s\" matches against \"%s\"", this.predicate, request)); 800 } 801 return Optional.of(this.handlerFunction); 802 } 803 else { 804 return Optional.empty(); 805 } 806 } 807 808 @Override 809 public void accept(Visitor visitor) { 810 visitor.route(this.predicate, this.handlerFunction); 811 } 812 813 } 814 815 private static final class DefaultNestedRouterFunction<T extends ServerResponse> 816 extends AbstractRouterFunction<T> { 817 818 private final RequestPredicate predicate; 819 820 private final RouterFunction<T> routerFunction; 821 822 public DefaultNestedRouterFunction(RequestPredicate predicate, RouterFunction<T> routerFunction) { 823 Assert.notNull(predicate, "Predicate must not be null"); 824 Assert.notNull(routerFunction, "RouterFunction must not be null"); 825 this.predicate = predicate; 826 this.routerFunction = routerFunction; 827 } 828 829 @Override 830 public Optional<HandlerFunction<T>> route(ServerRequest serverRequest) { 831 return this.predicate.nest(serverRequest) 832 .map(nestedRequest -> { 833 if (logger.isTraceEnabled()) { 834 logger.trace( 835 String.format( 836 "Nested predicate \"%s\" matches against \"%s\"", 837 this.predicate, serverRequest)); 838 } 839 Optional<HandlerFunction<T>> result = 840 this.routerFunction.route(nestedRequest); 841 if (result.isPresent() && nestedRequest != serverRequest) { 842 serverRequest.attributes().clear(); 843 serverRequest.attributes().putAll(nestedRequest.attributes()); 844 } 845 return result; 846 } 847 ) 848 .orElseGet(Optional::empty); 849 } 850 851 852 @Override 853 public void accept(Visitor visitor) { 854 visitor.startNested(this.predicate); 855 this.routerFunction.accept(visitor); 856 visitor.endNested(this.predicate); 857 } 858 859 } 860 861 private static class ResourcesRouterFunction extends AbstractRouterFunction<ServerResponse> { 862 863 private final Function<ServerRequest, Optional<Resource>> lookupFunction; 864 865 public ResourcesRouterFunction(Function<ServerRequest, Optional<Resource>> lookupFunction) { 866 Assert.notNull(lookupFunction, "Function must not be null"); 867 this.lookupFunction = lookupFunction; 868 } 869 870 @Override 871 public Optional<HandlerFunction<ServerResponse>> route(ServerRequest request) { 872 return this.lookupFunction.apply(request).map(ResourceHandlerFunction::new); 873 } 874 875 @Override 876 public void accept(Visitor visitor) { 877 visitor.resources(this.lookupFunction); 878 } 879 } 880 881 882}