1. 概述

过滤器在 Web 应用中非常普遍,它提供了一种在不修改接口逻辑的情况下修改请求或响应的机制。本文将介绍在 WebFlux 框架中实现过滤器的几种方式。

⚠️ 本文假设读者已具备 WebFlux 基础知识,如需了解框架细节,可参考相关文档

2. Maven 依赖

首先添加 WebFlux 的 Maven 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

3. 接口实现

我们需要创建两种类型的接口:基于注解和基于函数式编程的接口。

3.1 基于注解的控制器

@GetMapping(path = "/users/{name}")
public Mono<String> getName(@PathVariable String name) {
    return Mono.just(name);
}

3.2 基于函数式编程的接口

先创建处理器:

@Component
public class PlayerHandler {
    public Mono<ServerResponse> getName(ServerRequest request) {
        Mono<String> name = Mono.just(request.pathVariable("name"));
        return ok().body(name, String.class);
    }
}

再配置路由映射:

@Bean
public RouterFunction<ServerResponse> route(PlayerHandler playerHandler) {
    return RouterFunctions
      .route(GET("/players/{name}"), playerHandler::getName)
      .filter(new ExampleHandlerFilterFunction());
}

4. WebFlux 过滤器类型

WebFlux 提供两种过滤器:

  • WebFilter:全局生效,作用于所有接口
  • HandlerFilterFunction:仅作用于基于 Router 的接口

4.1 WebFilter 实现

实现一个为所有响应添加自定义头的过滤器:

@Component
public class ExampleWebFilter implements WebFilter {
 
    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, 
      WebFilterChain webFilterChain) {
        
        serverWebExchange.getResponse()
          .getHeaders().add("web-filter", "web-filter-test");
        return webFilterChain.filter(serverWebExchange);
    }
}

4.2 HandlerFilterFunction 实现

当 name 参数为 "test" 时返回 403 状态码:

public class ExampleHandlerFilterFunction 
  implements HandlerFilterFunction<ServerResponse, ServerResponse> {
 
    @Override
    public Mono<ServerResponse> filter(ServerRequest serverRequest,
      HandlerFunction<ServerResponse> handlerFunction) {
        if (serverRequest.pathVariable("name").equalsIgnoreCase("test")) {
            return ServerResponse.status(FORBIDDEN).build();
        }
        return handlerFunction.handle(serverRequest);
    }
}

5. 测试方法

使用 WebTestClient 进行过滤器测试:

5.1 测试注解式接口

@Test
public void whenUserNameIsBaeldung_thenWebFilterIsApplied() {
    EntityExchangeResult<String> result = webTestClient.get()
      .uri("/users/baeldung")
      .exchange()
      .expectStatus().isOk()
      .expectBody(String.class)
      .returnResult();

    assertEquals(result.getResponseBody(), "baeldung");
    assertEquals(
      result.getResponseHeaders().getFirst("web-filter"), 
      "web-filter-test");
}

@Test
public void whenUserNameIsTest_thenHandlerFilterFunctionIsNotApplied() {
    webTestClient.get().uri("/users/test")
      .exchange()
      .expectStatus().isOk();
}

5.2 测试函数式接口

@Test
public void whenPlayerNameIsBaeldung_thenWebFilterIsApplied() {
    EntityExchangeResult<String> result = webTestClient.get()
      .uri("/players/baeldung")
      .exchange()
      .expectStatus().isOk()
      .expectBody(String.class)
      .returnResult();

    assertEquals(result.getResponseBody(), "baeldung");
    assertEquals(
      result.getResponseHeaders().getFirst("web-filter"),
      "web-filter-test");
} 

@Test 
public void whenPlayerNameIsTest_thenHandlerFilterFunctionIsApplied() {
    webTestClient.get().uri("/players/test")
      .exchange()
      .expectStatus().isForbidden(); 
}

6. 总结

本文介绍了 WebFlux 中两种过滤器的实现方式:

  • WebFilter:全局过滤器,简单粗暴地处理所有请求
  • HandlerFilterFunction:针对特定路由的精细控制

实际开发中需要根据场景选择合适的过滤器类型。踩坑提醒:HandlerFilterFunction 仅对函数式路由生效,注解式接口不会触发!

完整示例代码可在 GitHub 获取。


原始标题:Spring Webflux Filters | Baeldung