1. 概述

本教程将深入探讨Micronaut框架提供的注解式HTTP过滤器。最初,Micronaut中的HTTP过滤器更接近Java EE的Filter接口和Spring Boot过滤器的实现方式。但随着最新主版本的发布,过滤器现在可以基于注解实现,将请求和响应过滤器分离。

本教程将重点研究Micronaut 4中引入的服务器端注解式过滤器方法。

2. HTTP过滤器基础

HTTP过滤器最初作为Java EE中的接口出现,是所有Java Web框架都实现的"规范"。官方文档定义:

过滤器是对资源请求(servlet或静态内容)或资源响应(或两者)执行过滤任务的对象。

实现Java EE接口的过滤器包含一个doFilter()方法,带有三个参数:ServletRequestServletResponseFilterChain。这让我们能访问请求和响应对象,并通过链式调用将请求/响应传递给下一个组件。时至今日,即使较新的框架仍可能使用相同或相似的名称和参数。

过滤器在实际开发中的常见应用场景:

  • ✅ 认证过滤器
  • ✅ 请求头过滤器(从请求中提取值或在响应中添加值)
  • ✅ 指标过滤器(如记录请求执行时间)
  • ✅ 日志过滤器

3. Micronaut中的HTTP过滤器

Micronaut的HTTP过滤器在某种程度上遵循了Java EE的Filter规范。例如,Micronaut的HttpFilter接口提供了一个*doFilter()*方法,包含请求对象和链对象参数。请求参数允许我们过滤请求,然后使用链对象处理并获取响应。最后,可以按需修改响应对象。

在Micronaut 4中,引入了一些新的过滤器注解,提供了仅针对请求、仅针对响应或两者兼得的过滤方法。

Micronaut为服务器端请求和响应提供了@ServerFilter*注解,同时也为REST客户端(针对第三方系统和微服务)提供了@ClientFilter注解。*

服务器端过滤器具备几个关键特性:

  • 支持路径匹配模式
  • 可排序(如认证过滤器必须优先执行)
  • 提供错误响应处理选项(如过滤异常)

接下来我们将深入探讨这些概念。

4. 过滤器模式

Micronaut中的HTTP过滤器基于路径匹配特定接口。要配置过滤器应用的接口,可设置路径匹配模式。 模式支持不同风格,如ANTREGEX,实际模式值如*/endpoint**。

模式风格有多种选择,默认使用AntPathMatcher,因为性能更优。正则表达式功能更强大但速度慢得多,仅在ANT不支持所需模式时作为最后选择。

常用模式示例:

  • /** 匹配任意路径
  • /filters-annotations/** 匹配filters-annotations下的所有路径(如/filters-annotations/endpoint1)
  • /filters-annotations/*1 匹配filters-annotations下以'1'结尾的路径
  • **/endpoint1 匹配所有以'endpoint1'结尾的路径
  • **/endpoint* 匹配所有以'endpoint'开头后接任意内容的路径

默认FilterPatternStyle.ANT风格中:

  • * 匹配零个或多个字符
  • ** 匹配路径中的零个或多个子目录

5. Micronaut中的注解式服务器过滤器

Micronaut 4引入了注解式HTTP过滤器(也称为过滤器方法)。过滤器方法允许我们将请求和响应过滤器分离。 在注解式过滤器出现前,我们只能用同一种方式定义请求和响应过滤器。现在可以分离关注点,使代码更清晰易读。

需要同时处理请求和响应时,仍可使用FilterContinuation实现。

5.1 过滤器方法

根据过滤需求(请求/响应),可使用@RequestFilter*或@ResponseFilter注解。* 类级别仍需*@ServerFilter*注解定义过滤器,路径匹配和排序也在类级别设置。每个过滤器方法也可单独应用路径模式。

下面创建一个包含请求过滤和响应过滤方法的ServerFilter示例:

@Slf4j
@ServerFilter(patterns = { "**/endpoint*" })
public class CustomFilter implements Ordered {
    @RequestFilter
    @ExecuteOn(TaskExecutors.BLOCKING)
    public void filterRequest(HttpRequest<?> request) {
        String customRequestHeader = request.getHeaders()
          .get(CUSTOM_HEADER_KEY);
        log.info("request header: {}", customRequestHeader);
    }

    @ResponseFilter
    public void filterResponse(MutableHttpResponse<?> res) {
        res.getHeaders()
          .add(X_TRACE_HEADER_KEY, "true");
    }
}

filterRequest()方法使用@RequestFilter注解,接收HTTPRequest参数。这允许我们访问请求对象并读取/记录请求头。实际应用中,这里可能会根据请求头值拒绝请求。

filterResponse()方法使用@ResponseFilter注解,接收MutableHttpResponse参数(即将返回给客户端的响应对象)。在响应前,该方法向响应添加了一个请求头。

注意:请求可能已被更低序号的过滤器处理,后续还会被更高序号的过滤器处理。响应则相反:高序号过滤器先处理,低序号后处理(详见过滤器排序部分)。

5.2 连续过滤器

过滤器方法使代码整洁,但有时仍需在同一方法中处理请求和响应。Micronaut提供了连续过滤器(continuations)来满足这种需求。 方法注解仍为*@RequestFilter,但参数不同,类级别仍需@ServerFilter*注解。

典型场景是分布式系统中的请求追踪:需要从请求中提取追踪ID,并在响应中返回相同ID:

@Slf4j
@ServerFilter(patterns = { "**/endpoint*" })
@Order(1)
public class RequestIDFilter implements Ordered {
    @RequestFilter
    @ExecuteOn(TaskExecutors.BLOCKING)
    public void filterRequestIDHeader(
        HttpRequest<?> request, 
        FilterContinuation<MutableHttpResponse<?>> continuation
    ) {
        String requestIdHeader = request.getHeaders().get(REQUEST_ID_HEADER_KEY);
        if (requestIdHeader == null || requestIdHeader.trim().isEmpty()) {
            requestIdHeader = UUID.randomUUID().toString();
            log.info(
                "request ID not received. Created and will return one with value: [{}]", 
                requestIdHeader
            );
        } else {
            log.info("request ID received. Request ID: [{}]", requestIdHeader);
        }

        MutableHttpResponse<?> res = continuation.proceed();

        res.getHeaders().add(REQUEST_ID_HEADER_KEY, requestIdHeader);
    }
}

filterRequestIDHeader()方法使用@RequestFilter注解,接收HttpRequestFilterContinuation参数。我们检查请求中的"Request-ID"头,不存在则生成新ID并记录。

通过*continuation.proceed()*获取响应对象,然后将相同ID添加到响应头,供客户端后续追踪使用。

5.3 过滤器排序

许多场景需要特定过滤器按顺序执行。Micronaut提供两种排序方式:*@Order注解或实现Ordered*接口(均在类级别)。

排序原理:通过int值指定执行顺序。请求过滤器顺序直观:序号-5先于2执行,2先于4执行。响应过滤器则相反:序号4最先应用,然后是2,最后是-5。

实现接口时需手动覆盖*getOrder()*方法(默认值为0):

@Filter(patterns = { "**/*1" })
public class PrivilegedUsersEndpointFilter implements HttpServerFilter, Ordered {
    // 过滤器方法省略

    @Override
    public int getOrder() {
        return 3;
    }
}

使用注解时只需设定值:

@ServerFilter(patterns = { "**/endpoint*" })
@Order(1)
public class RequestIDFilter implements Ordered {
    // 过滤器方法省略
}

⚠️ 踩坑提醒:同时使用*@Order注解和Ordered*接口可能导致异常行为,建议统一使用其中一种方式。

6. 总结

本教程探讨了过滤器的通用概念及Micronaut中的HTTP过滤器实现。我们了解了过滤器的多种实现方式和实际应用场景,展示了仅请求过滤、仅响应过滤及两者兼得的注解式过滤器示例。最后深入解析了路径匹配和过滤器排序等关键概念。

完整源代码请参考GitHub仓库


原始标题:Annotation Based HTTP Filters in Micronaut | Baeldung