【发布时间】:2019-09-19 20:38:38
【问题描述】:
我来自 Node-ExpressJS,所以我熟悉中间件的概念。在学习 Spring 的过程中,我知道了一个名为 Filter 的组件,它的作用类似于 Express 中的中间件,但有一些区别。
所以我试图了解 Filter 和 FilterChain 在 Spring 中的实际工作原理。
我有以下代码:
Filter1.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 +++++++++");
}
}
Filter2.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 ++++++++++");
}
}
Controller.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) 的来电和这件事有关系吗?一旦调用了FilterChain 中的doFilter,似乎就无法修改Filter1 类中的response 对象。
我想在这里理解的是:
如果需要调用
FilterChain.doFilter以将request对象传播到下一个过滤器并最终传播到控制器,则不应允许在调用后修改response对象chain.doFilter退货?它在内部究竟是如何工作的?调用如何向下传播到控制器,然后返回到第一个过滤器?另外,如果 Filter1 想查看从 Filter2 返回的响应正文并可能对其进行修改,它会怎么做? p>
【问题讨论】:
-
这里没有魔法 - 对
doFilter的调用是阻塞。当它返回(正常或异常)时,下游过滤器和请求处理器本身就完成了。把它想象成俄罗斯套娃,而不是链条。 -
您的代码不起作用的原因与此完全相关 - 一旦
doFilter返回主请求处理器已完成 - 它很可能已刷新流并发送内容。一旦标头已经发送,您就不能再发送更多标头了 - 没有通过 TCP 的引用传递!如果您想在之后添加标头,则需要缓冲整个响应 - 这可能非常昂贵甚至不可能。
标签: java spring spring-boot servlet-filters