java - Spring 中 Servlet 过滤器的内部工作原理
问题描述
我来自Node-ExpressJS,所以我熟悉中间件的概念。在我学习 Spring 的过程中,我知道了一个名为的组件Filter
,它的作用类似于 Express 中的中间件,但有一些不同之处。
所以我试图了解 aFilter
和FilterChain
实际在 Spring 中的工作方式。
我有以下代码:
过滤器1.java
@Component
@Order(1)
public class Filter1 implements Filter {
.....
.....
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOGGER.info("############# Invoking Filter1 ############");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
LOGGER.info("************ Moving on to next Filter");
LOGGER.info("Adding new Attribute");
req.setAttribute("Custom_Attribute_1", "TEST***TEST***TEST");
chain.doFilter(request, response);
resp.addHeader("1st Header", "1ST"); // Custom header that never shows up
LOGGER.info("+++++++++ GOING BACK FROM Filter1 +++++++++");
}
}
过滤器2.java
@Component
@Order(2)
public class Filter2 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOGGER.info("############# Invoking Filter2 ####################");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
req.setAttribute("Filter2 Attribute", "2nd ORDER");
resp.addHeader("2nd Header", "2ND"); //Custom header that actually shows up
chain.doFilter(request, response);
LOGGER.info("+++++++++++ GOING BACK FROM Filter2 ++++++++++");
}
}
控制器.java
@RestController
public class Controller {
@GetMapping("/")
public ResponseEntity<Object> createResource(HttpServletRequest req) {
return new ResponseEntity<Object>("Resource Created",HttpStatus.OK);
}
}
当我使用 Postman 向我的控制器发送请求时,我只能在响应中看到我的一个自定义标头,即2nd Header
,但看不到另一个标头。
Postman 中的响应标头
2nd Header → 2ND
Content-Type → text/plain;charset=UTF-8
Content-Length → 15
Date → Thu, 19 Sep 2019 20:16:25 GMT
的呼唤chain.doFilter(request, response)
与它有什么关系吗?似乎一旦调用了类中的response
对象就无法修改。Filter1
doFilter
FilterChain
我想在这里理解的是:
如果
FilterChain.doFilter
需要调用什么来将request
对象传播到下一个过滤器并最终传播到控制器,那么response
一旦调用返回,是否应该允许修改对象chain.doFilter
?它在内部究竟是如何工作的?调用如何向下传播到控制器,然后返回到第一个过滤器?此外,如果Filter1想在从Filter2返回后查看响应的正文并可能对其进行修改,它会如何做呢?
解决方案
基本上,如javadoc中所述,doFilter()
将请求和响应对象发送到 FilterChain 中的下一个过滤器。
在您的第一个示例中,在添加“第一个标题”之前,您调用链中的下一个过滤器。这就是为什么你一开始就没有得到“第一个标题”的原因。此请求将进入控制器层,然后由您的控制器进行评估。当控制器处理完对象后,响应开始填充到过滤器。
所以你的代码是这样工作的
arrived Filter1 -> Filter 2 > add "2st Header" > .. > Controller > Controller runs and prepares a response object.
Controller Response > .... > Filter 2 > Filter 1 > add "1st Header" > ...
因此,当请求到达您的控制器时,控制器在请求上下文中永远不会有“第一个标头”。
此外,如果 Filter1 想在从 Filter2 返回后查看响应的正文并可能对其进行修改,它会如何做呢?
我没有尝试过,但你应该看看这个线程,它看起来像你想要实现的。
推荐阅读
- javascript - 即使输入设置为“必需”,我的按钮仍在工作
- google-apps-script - 在工作表中插入我自己的联系方式 - Google Apps 脚本
- automapper - 使用自动映射 IF 忽略属性
- sql - 在起始值不是 1 的插入语句上分配唯一编号
- c# - C#:持有命名信号量的进程在没有释放的情况下死亡
- node.js - 我写的CLI不返回错误,正常返回错误文本
- c# - HttpListener 打开后忽略 websocket 请求
- styled-components - 样式组件不能与 scss bem 结构一起正常工作
- python - 使用 grid_mapping ValueError 保存 NetCDF
- python - 将列表中的每个元素与之前的所有连续元素进行比较