Spring中Servlet过滤器的内部工作原理

时间:2019-09-19 20:38:38

标签: java spring spring-boot servlet-filters

我来自Node-ExpressJS,所以我熟悉中间件的概念。在学习Spring的过程中,我知道了一个名为Filter的组件,该组件的行为与Express中的中间件类似,只是有一些区别。

因此,我试图了解FilterFilterChain在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,但看不到另一个标头。

邮递员中的回复标题

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的{​​{1}},就无法对Filter1类中的doFilter对象进行修改。

我想在这里理解的是:

  1. 如果需要调用FilterChain来将FilterChain.doFilter对象传播到下一个过滤器并最终传播到控制器,则不应允许request对象response的调用返回后会被修改吗?它内部如何工作?调用如何将所有信息传播到控制器,然后再返回到第一个过滤器?

  2. 此外,如果 Filter1 想要查看从 Filter2 返回的响应的正文并可能对其进行修改,它会如何做?

1 个答案:

答案 0 :(得分:0)

基本上,doFilter()将请求和响应对象发送到javadoc中提到的F​​ilterChain中的下一个Filter。

在第一个示例中,在添加“第一个标头”之前,您将调用tha链中的下一个过滤器。这就是为什么您首先没有获得“ 1st Header”的原因。该请求将进入控制器层,然后由您的控制器进行评估。当控制器完成对对象的处理后,响应开始填充并返回到过滤器。

所以您的代码是这样的

arrived Filter1 -> Filter 2 > add "2st Header" > .. > Controller > Controller runs and prepares a response object.

Controller Response > .... > Filter 2 > Filter 1 > add "1st Header" > ...

因此,当请求到达您的控制器时,控制器在请求上下文中将永远不会有“ 1st Header”。

  

此外,如果Filter1希望在从Filter2返回并可以修改它之后看到响应的正文,那么它将如何处理?

我还没有尝试过,但是您应该看看this thread,它看起来像您想要实现的目标。