函数式端点
什么是函数式端点
函数式端点(Functional Endpoints)是 WebFlux 提供的另一种编程模型,使用函数式风格定义路由和处理器,相比注解式更加灵活。
核心组件
- HandlerFunction:处理请求的函数,类似于
@RequestMapping 方法
- RouterFunction:路由函数,将请求映射到 HandlerFunction
HandlerFunction
HandlerFunction<T extends ServerResponse> 是一个函数式接口:
1 2 3 4
| @FunctionalInterface public interface HandlerFunction<T extends ServerResponse> { Mono<T> handle(ServerRequest request); }
|
定义 Handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| @Component public class UserHandler { private final UserService userService; public UserHandler(UserService userService) { this.userService = userService; } public Mono<ServerResponse> getAllUsers(ServerRequest request) { return ServerResponse.ok() .contentType(MediaType.APPLICATION_JSON) .body(userService.findAll(), User.class); } public Mono<ServerResponse> getUserById(ServerRequest request) { String id = request.pathVariable("id"); return userService.findById(id) .flatMap(user -> ServerResponse.ok() .contentType(MediaType.APPLICATION_JSON) .bodyValue(user)) .switchIfEmpty(ServerResponse.notFound().build()); } public Mono<ServerResponse> createUser(ServerRequest request) { return request.bodyToMono(User.class) .flatMap(userService::save) .flatMap(user -> ServerResponse .created(URI.create("/api/users/" + user.getId())) .contentType(MediaType.APPLICATION_JSON) .bodyValue(user)); } public Mono<ServerResponse> updateUser(ServerRequest request) { String id = request.pathVariable("id"); return request.bodyToMono(User.class) .flatMap(user -> userService.update(id, user)) .flatMap(user -> ServerResponse.ok() .contentType(MediaType.APPLICATION_JSON) .bodyValue(user)) .switchIfEmpty(ServerResponse.notFound().build()); } public Mono<ServerResponse> deleteUser(ServerRequest request) { String id = request.pathVariable("id"); return userService.deleteById(id) .then(ServerResponse.noContent().build()); } }
|
RouterFunction
RouterFunction<T extends ServerResponse> 用于定义路由规则。
基本路由定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration public class RouterConfig { @Bean public RouterFunction<ServerResponse> userRoutes(UserHandler handler) { return RouterFunctions.route() .GET("/api/users", handler::getAllUsers) .GET("/api/users/{id}", handler::getUserById) .POST("/api/users", handler::createUser) .PUT("/api/users/{id}", handler::updateUser) .DELETE("/api/users/{id}", handler::deleteUser) .build(); } }
|
使用 nest 组织路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Bean public RouterFunction<ServerResponse> routes(UserHandler userHandler, OrderHandler orderHandler) { return RouterFunctions.route() .nest(path("/api/users"), builder -> builder .GET("", userHandler::getAllUsers) .GET("/{id}", userHandler::getUserById) .POST("", userHandler::createUser) .PUT("/{id}", userHandler::updateUser) .DELETE("/{id}", userHandler::deleteUser) ) .nest(path("/api/orders"), builder -> builder .GET("", orderHandler::getAllOrders) .GET("/{id}", orderHandler::getOrderById) .POST("", orderHandler::createOrder) ) .build(); }
|
添加过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Bean public RouterFunction<ServerResponse> routes(UserHandler handler) { return RouterFunctions.route() .GET("/api/users", handler::getAllUsers) .GET("/api/users/{id}", handler::getUserById) .filter((request, next) -> { log.info("Request: {} {}", request.method(), request.path()); return next.handle(request) .doOnNext(response -> { log.info("Response status: {}", response.statusCode()); }); }) .build(); }
|
条件路由
1 2 3 4 5 6 7 8 9 10
| @Bean public RouterFunction<ServerResponse> routes(UserHandler handler) { return RouterFunctions.route() .route(GET("/api/users").and(accept(MediaType.APPLICATION_JSON)), handler::getAllUsers) .route(GET("/api/users").and(accept(MediaType.TEXT_EVENT_STREAM)), handler::streamUsers) .build(); }
|
RequestPredicate
RequestPredicate 用于定义请求匹配条件。
常用谓词
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import static org.springframework.web.reactive.function.server.RequestPredicates.*;
GET("/path") POST("/path") PUT("/path") DELETE("/path")
accept(MediaType.APPLICATION_JSON) contentType(MediaType.APPLICATION_JSON)
path("/api/**") pathExtension("json")
queryParam("name", value -> value.length() > 0)
headers(headers -> headers.header("X-Custom").isPresent())
GET("/api/users").and(accept(MediaType.APPLICATION_JSON)) GET("/api/users").or(GET("/users"))
|
请求验证
手动验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public Mono<ServerResponse> createUser(ServerRequest request) { return request.bodyToMono(User.class) .flatMap(user -> { List<String> errors = validate(user); if (!errors.isEmpty()) { return ServerResponse.badRequest() .bodyValue(new ValidationError(errors)); } return userService.save(user) .flatMap(saved -> ServerResponse.created( URI.create("/api/users/" + saved.getId())) .bodyValue(saved)); }); }
private List<String> validate(User user) { List<String> errors = new ArrayList<>(); if (user.getName() == null || user.getName().isBlank()) { errors.add("Name is required"); } if (user.getEmail() == null || !user.getEmail().contains("@")) { errors.add("Valid email is required"); } return errors; }
|
使用 Validator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Component public class UserHandler { private final Validator validator; private final UserService userService; public Mono<ServerResponse> createUser(ServerRequest request) { return request.bodyToMono(User.class) .flatMap(user -> { Errors errors = new BeanPropertyBindingResult(user, "user"); validator.validate(user, errors); if (errors.hasErrors()) { return ServerResponse.badRequest() .bodyValue(errors.getAllErrors()); } return userService.save(user) .flatMap(saved -> ServerResponse.created( URI.create("/api/users/" + saved.getId())) .bodyValue(saved)); }); } }
|
异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Bean public RouterFunction<ServerResponse> routes(UserHandler handler) { return RouterFunctions.route() .GET("/api/users/{id}", handler::getUserById) .onError(NotFoundException.class, (e, request) -> ServerResponse.notFound().build()) .onError(ValidationException.class, (e, request) -> ServerResponse.badRequest().bodyValue(e.getMessage())) .onError(Exception.class, (e, request) -> ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR) .bodyValue("Internal server error")) .build(); }
|
函数式 vs 注解式
| 特性 |
注解式 |
函数式 |
| 代码风格 |
声明式 |
函数式 |
| 路由定义 |
分散在各个方法 |
集中在一处 |
| 灵活性 |
较低 |
较高 |
| 学习曲线 |
较低 |
较高 |
| 测试 |
需要 Spring 上下文 |
可以单元测试 |
| 适用场景 |
传统 CRUD |
复杂路由逻辑 |
相关链接