JAX-RS 过滤器是在 HTTP 资源前后插入处理的标准方式。这篇用一个真实的 Jersey 服务,把请求与响应链的顺序、PreMatching、abortWith、异常映射、名字绑定和请求体读取这些容易混淆的行为逐个量出来。
GET /hello → HTTP 200,响应体 hello
请求:PreMatching A(100) 路径=hello
请求:PreMatching B(200) 方法=GET
请求:Post First(100)
请求:Post A(300) 已匹配资源方法=hello
请求:Post Last(400)
资源:hello() 被调用
响应:Resp(300) 状态=200
响应:Resp(100) 状态=200,实体=hello
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后) GET /legacy/hello(PreMatching A 改写路径) → HTTP 200,响应体 hello
请求:PreMatching A(100) 路径=legacy/hello
PreMatching A 把 legacy/hello 改写成 hello
请求:PreMatching B(200) 方法=GET
请求:Post First(100)
请求:Post A(300) 已匹配资源方法=hello
请求:Post Last(400)
资源:hello() 被调用
响应:Resp(300) 状态=200
响应:Resp(100) 状态=200,实体=hello
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后)
POST /echo + X-Method-Override(PreMatching B 把 POST 改成 PUT) → HTTP 200,响应体 put:abc
请求:PreMatching A(100) 路径=echo
请求:PreMatching B(200) 方法=POST
PreMatching B 把 POST 改成 PUT
请求:Post First(100)
请求:Post A(300) 已匹配资源方法=put
请求:Post Last(400)
资源:put() 被调用,body 长度 3
响应:Resp(300) 状态=200
响应:Resp(100) 状态=200,实体=put:abc
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后)
GET /nope(没有匹配的资源) → HTTP 404,响应体
请求:PreMatching A(100) 路径=nope
请求:PreMatching B(200) 方法=GET
响应:Resp(300) 状态=404
响应:Resp(100) 状态=404
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后) GET /hello + X-Mode: deny(Post First 调用 abortWith) → HTTP 403,响应体 被过滤器拒绝
请求:PreMatching A(100) 路径=hello
请求:PreMatching B(200) 方法=GET
请求:Post First(100)
Post First 调用 abortWith(403)
响应:Resp(300) 状态=403
响应:Resp(100) 状态=403,实体=被过滤器拒绝
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后)
GET /hello + X-Mode: throw(Post Last 抛异常) → HTTP 500,响应体 映射后的异常:请求过滤器抛出
请求:PreMatching A(100) 路径=hello
请求:PreMatching B(200) 方法=GET
请求:Post First(100)
请求:Post A(300) 已匹配资源方法=hello
请求:Post Last(400)
Post Last 抛出 IllegalStateException
响应:Resp(300) 状态=500
响应:Resp(100) 状态=500,实体=映射后的异常:请求过滤器抛出
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后)
GET /boom(资源抛异常,有 ExceptionMapper) → HTTP 500,响应体 映射后的异常:资源抛出
请求:PreMatching A(100) 路径=boom
请求:PreMatching B(200) 方法=GET
请求:Post First(100)
请求:Post A(300) 已匹配资源方法=boom
请求:Post Last(400)
资源:boom() 抛出 IllegalStateException
响应:Resp(300) 状态=500
响应:Resp(100) 状态=500,实体=映射后的异常:资源抛出
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后) GET /audited(@Audited 绑定的过滤器) → HTTP 200,响应体 audited
请求:PreMatching A(100) 路径=audited
请求:PreMatching B(200) 方法=GET
请求:Post First(100)
请求:Post A(300) 已匹配资源方法=audited
请求:Post Last(400)
请求:Audit 过滤器(名字绑定 @Audited)
资源:audited() 被调用
响应:Resp(300) 状态=200
响应:Resp(100) 状态=200,实体=audited
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后)
GET /hello(没有 @Audited,不会运行 Audit 过滤器) → HTTP 200,响应体 hello
请求:PreMatching A(100) 路径=hello
请求:PreMatching B(200) 方法=GET
请求:Post First(100)
请求:Post A(300) 已匹配资源方法=hello
请求:Post Last(400)
资源:hello() 被调用
响应:Resp(300) 状态=200
响应:Resp(100) 状态=200,实体=hello
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后)
PUT /echo,过滤器读取了请求体但没放回 → HTTP 200,响应体 put:
请求:PreMatching A(100) 路径=echo
请求:PreMatching B(200) 方法=PUT
请求:Post First(100)
请求:BodyPeek 读取了请求体 11 字节
请求:Post A(300) 已匹配资源方法=put
请求:Post Last(400)
资源:put() 被调用,body 长度 0
响应:Resp(300) 状态=200
响应:Resp(100) 状态=200,实体=put:
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后)
PUT /echo,过滤器读取后又把字节放回 → HTTP 200,响应体 put:payload-123
请求:PreMatching A(100) 路径=echo
请求:PreMatching B(200) 方法=PUT
请求:Post First(100)
请求:BodyPeek 读取了请求体 11 字节
BodyPeek 把读出来的字节重新放回实体流
请求:Post A(300) 已匹配资源方法=put
请求:Post Last(400)
资源:put() 被调用,body 长度 11
响应:Resp(300) 状态=200
响应:Resp(100) 状态=200,实体=put:payload-123
响应:TraceEmit(10) 写出 X-Trace(响应过滤器按优先级从大到小执行,10 最后)