4.2 函数式路由

Spring WebFlux 函数式路由:RouterFunctions 构建路由表、RequestPredicates 路径/方法/头匹配、HandlerFunction 处理请求、嵌套路由与 filter 组合,以及与注解路由的适用场景对比。

为什么需要函数式路由

注解路由很直观,但有两个局限:

  1. 路由定义分散在每个 controller 类中,集中查看全部路由需要扫描多个文件
  2. 运行时组合困难——无法根据配置动态添加/删除路由

函数式路由把所有路由定义集中在一个(或几个)RouterFunction bean 中,纯 Java 代码表达路由逻辑。

最小函数式路由

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.*;
import reactor.core.publisher.Mono;

@Configuration
public class OrderRouter {

    @Bean
    public RouterFunction<ServerResponse> orderRoutes() {
        return RouterFunctions.route()
            .GET("/api/orders", request ->
                ServerResponse.ok()
                    .bodyValue("orders list"))
            .GET("/api/orders/{id}", request -> {
                String id = request.pathVariable("id");
                return ServerResponse.ok()
                    .bodyValue("order " + id);
            })
            .POST("/api/orders", request ->
                ServerResponse.ok()
                    .bodyValue("created"))
            .build();
    }
}

RouterFunctions.route() 是可变构建器,链式添加路由规则。.build() 返回不可变的 RouterFunction。

RequestPredicates 匹配

除了简单路径匹配,可以用 RequestPredicates 做复杂条件:

import static org.springframework.web.reactive.function.server.RequestPredicates.*;

@Bean
public RouterFunction<ServerResponse> advancedRoutes() {
    return RouterFunctions.route()
        // 路径 + 方法
        .nest(path("/api/users"), builder -> builder
            .GET("/{id}", this::getUser)
            .POST("", this::createUser)
        )
        // 请求头匹配
        // header 缺失时 h.header(...) 返回 null,常量在前避免 NPE
        .route(headers(h -> "v2".equals(h.header("X-Api-Version"))), this::v2Handler)
        // 查询参数匹配
        .route(queryParam("format", "csv"), this::csvHandler)
        // Content-Type 匹配
        .route(contentType(MediaType.APPLICATION_JSON), this::jsonHandler)
        .build();
}

Mono<ServerResponse> getUser(ServerRequest req) {
    String id = req.pathVariable("id");
    return ServerResponse.ok()
        .contentType(MediaType.APPLICATION_JSON)
        .bodyValue(new User(Long.parseLong(id), "user-" + id));
}

Mono<ServerResponse> createUser(ServerRequest req) {
    return req.bodyToMono(User.class)
        .flatMap(user -> ServerResponse.ok()
            .bodyValue("Created: " + user.name()));
}

请求体读取

函数式路由中从 ServerRequest 读取请求体:

// 读取为 Mono
Mono<User> userMono = request.bodyToMono(User.class);

// 读取为 Flux
Flux<User> userFlux = request.bodyToFlux(User.class);

// 读取为字符串
Mono<String> body = request.bodyToMono(String.class);

// 读取原始 DataBuffer(流式处理大文件)
Flux<DataBuffer> buffers = request.body(BodyExtractors.toDataBuffers());

响应构建

ServerResponse 提供丰富的响应构建 API:

// 200 OK + JSON
ServerResponse.ok()
    .contentType(MediaType.APPLICATION_JSON)
    .bodyValue(object);

// 201 Created + Location 头
ServerResponse.created(URI.create("/api/orders/123"))
    .bodyValue(createdOrder);

// 204 No Content
ServerResponse.noContent().build();

// 404 Not Found
ServerResponse.notFound().build();

// 400 Bad Request
ServerResponse.badRequest()
    .bodyValue(Map.of("error", "Invalid input"));

// 503 Service Unavailable
ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
    .bodyValue(Map.of("retry_after", 30));

嵌套路由

nest() 可以给一组路由加统一前缀:

@Bean
public RouterFunction<ServerResponse> apiRoutes() {
    return RouterFunctions.route()
        .nest(path("/api/v1"), v1 -> v1
            .nest(path("/users"), users -> users
                .GET("", this::listUsers)
                .GET("/{id}", this::getUser)
                .POST("", this::createUser)
            )
            .nest(path("/orders"), orders -> orders
                .GET("", this::listOrders)
                .GET("/{id}", this::getOrder)
            )
        )
        .build();
}

这种结构在大型 API 中比分散的注解 controller 更易维护——路由层级一目了然。

函数式过滤器

函数式路由也可以加横切逻辑:

@Bean
public RouterFunction<ServerResponse> filteredRoutes() {
    RouterFunction<ServerResponse> routes = RouterFunctions.route()
        .GET("/api/protected", this::protectedHandler)
        .build();

    return routes.filter((request, next) -> {
        long start = System.nanoTime();
        System.out.println("→ " + request.method() + " " + request.path());
        return next.handle(request)
            .doFinally(sig -> {
                long ms = (System.nanoTime() - start) / 1_000_000;
                System.out.println("← " + request.path() + " in " + ms + "ms");
            });
    });
}

.filter() 的参数是 (ServerRequest, HandlerFunction) -> Mono<ServerResponse>,与第 1.2 节的中间件模式完全一致。

注解路由 vs 函数式路由

维度注解路由函数式路由
代码组织分散在多个 @Controller 类集中在 RouterFunction bean
路由查看需扫描注解路由表在代码中显式可见
动态路由困难可在运行时组装
IDE 重构字符串路径易遗漏路径常量可安全重构
团队熟悉度高(Spring MVC 用户熟悉)需额外学习成本
与 Spring 生态AOP、@Valid 等开箱即用部分功能需手动配置

eventd v2 的 API 层使用混合策略:

  • 主要业务接口(用户、订单、事件)用注解路由——开发效率高
  • 网关/internal 接口(健康检查、metrics、动态配置)用函数式路由——便于集中管理

从函数式路由调用 service

@Service
public class OrderService {
    public Mono<Order> findById(Long id) { ... }
    public Flux<Order> findAll() { ... }
    public Mono<Order> create(Order order) { ... }
}

@Component
public class OrderHandler {
    private final OrderService service;

    public OrderHandler(OrderService service) {
        this.service = service;
    }

    public Mono<ServerResponse> list(ServerRequest req) {
        return ServerResponse.ok()
            .body(service.findAll(), Order.class);
    }

    public Mono<ServerResponse> get(ServerRequest req) {
        Long id = Long.parseLong(req.pathVariable("id"));
        return service.findById(id)
            .flatMap(order -> ServerResponse.ok().bodyValue(order))
            .switchIfEmpty(ServerResponse.notFound().build());
    }
}

Handler 类(纯函数)+ Service 类(业务逻辑)的分层与注解路由的 Controller + Service 分层等价。

小结

能力API说明
定义路由RouterFunctions.route().GET(...).POST(...).build()链式构建
路径匹配path("/api/...")nest() 加前缀
条件匹配queryParam()、headers()、contentType()复杂路由条件
读取请求体request.bodyToMono() / bodyToFlux()与 @RequestBody 等价
构建响应ServerResponse.ok() / notFound() / badRequest()流式 API
过滤器.filter((req, next) -> ...)与中间件模式一致

下一节 4.3 WebFlux 过滤器 :WebFilter、HandlerFilterFunction、顺序控制、与函数式路由 filter 的对比。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. 15.3 全书总结与学习路径回顾
  2. 15.2 响应式流处理的演进趋势
  3. 15.1 引擎选型指南