函数式端点

函数式端点

什么是函数式端点

函数式端点(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()
// 使用 RequestPredicate 进行条件匹配
.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.*;

// HTTP 方法
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 复杂路由逻辑

相关链接

  • 上一篇:WebFlux核心组件
  • 下一篇:实战案例

函数式端点
https://zmmmmy.github.io/2026/01/10/函数式端点/
作者
ZhiMy
发布于
2026年1月10日
许可协议