1. 概述
本教程将深入探讨Micronaut框架提供的注解式HTTP过滤器。最初,Micronaut中的HTTP过滤器更接近Java EE的Filter接口和Spring Boot过滤器的实现方式。但随着最新主版本的发布,过滤器现在可以基于注解实现,将请求和响应过滤器分离。
本教程将重点研究Micronaut 4中引入的服务器端注解式过滤器方法。
2. HTTP过滤器基础
HTTP过滤器最初作为Java EE中的接口出现,是所有Java Web框架都实现的"规范"。官方文档定义:
过滤器是对资源请求(servlet或静态内容)或资源响应(或两者)执行过滤任务的对象。
实现Java EE接口的过滤器包含一个doFilter()方法,带有三个参数:ServletRequest、ServletResponse和FilterChain。这让我们能访问请求和响应对象,并通过链式调用将请求/响应传递给下一个组件。时至今日,即使较新的框架仍可能使用相同或相似的名称和参数。
过滤器在实际开发中的常见应用场景:
- ✅ 认证过滤器
- ✅ 请求头过滤器(从请求中提取值或在响应中添加值)
- ✅ 指标过滤器(如记录请求执行时间)
- ✅ 日志过滤器
3. Micronaut中的HTTP过滤器
Micronaut的HTTP过滤器在某种程度上遵循了Java EE的Filter规范。例如,Micronaut的HttpFilter接口提供了一个*doFilter()*方法,包含请求对象和链对象参数。请求参数允许我们过滤请求,然后使用链对象处理并获取响应。最后,可以按需修改响应对象。
在Micronaut 4中,引入了一些新的过滤器注解,提供了仅针对请求、仅针对响应或两者兼得的过滤方法。
Micronaut为服务器端请求和响应提供了@ServerFilter*注解,同时也为REST客户端(针对第三方系统和微服务)提供了@ClientFilter注解。*
服务器端过滤器具备几个关键特性:
- 支持路径匹配模式
- 可排序(如认证过滤器必须优先执行)
- 提供错误响应处理选项(如过滤异常)
接下来我们将深入探讨这些概念。
4. 过滤器模式
Micronaut中的HTTP过滤器基于路径匹配特定接口。要配置过滤器应用的接口,可设置路径匹配模式。 模式支持不同风格,如ANT或REGEX,实际模式值如*/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注解,接收HttpRequest和FilterContinuation参数。我们检查请求中的"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仓库。