【问题标题】:How to create multiple asynchronous java filters?如何创建多个异步java过滤器?
【发布时间】:2014-09-04 22:57:15
【问题描述】:

我正在尝试创建一个具有多个异步过滤器的 Java 应用程序,但似乎无法让它们很好地协同工作。我认为主要问题在于 run() 方法,我不知道如何将请求传递给链中的下一个过滤器。我试过chain.doFilter(request, response),但这似乎不起作用,AsyncContext 上有dispatch()complete() API,但它们似乎关闭了整个AsyncContext。似乎必须有另一种方法才能让它发挥作用。下面是我正在使用的过滤器的 sn-p - 第二个过滤器看起来几乎相同。

注意:我正在添加标头以尝试找出被调用的内容。

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        final AsyncContext asyncContext = request.startAsync();
        final HttpServletResponse res = (HttpServletResponse) response;
        asyncContext.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent event) throws IOException {
                res.addHeader("S-AST2", "onComplete");
            }

            @Override
            public void onTimeout(AsyncEvent event) throws IOException {
                res.addHeader("S-AST3", "onTimeout");
            }

            @Override
            public void onError(AsyncEvent event) throws IOException {
                res.addHeader("S-AST4", "onError");
            }

            @Override
            public void onStartAsync(AsyncEvent event) throws IOException {
                res.addHeader("S-AST0", "onStartAsync");
            }
        });

        asyncContext.start(new Runnable() {
            @Override
            public void run() {
                res.addHeader("S-AST1", "before");
                // This doesn't seem to work...
                asyncContext.dispatch();
                // ... or this ...
                asyncContext.complete();
                // ... or this ...
                chain.doFilter(request, response);
            }
        });
    }

感谢您的任何见解!

【问题讨论】:

    标签: java servlets asynchronous filter servlet-filters


    【解决方案1】:

    我需要装饰响应,但我不知道底层 servlet 是否正在执行异步,或者它是否已经完成。在 Jetty 9.1.x 上,我通过期待 IllegalStateException
    解决了它 以下示例说明了如何包装响应(使用自定义 BufferingHttpServletResponseWrapper 来缓冲所有写入响应的内容)以拦截输入,以便对其进行修饰。

         @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
         final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
         final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    
         // Buffer the output to a string in order to calculate its signature and add the signature to a header before it's sent to the client
         final BufferingHttpServletResponseWrapper responseWrapper = new BufferingHttpServletResponseWrapper(httpServletResponse);
    
         chain.doFilter(httpServletRequest, responseWrapper);
    
         // This is the only way I can see that will safely let us know if we should treat this as an active async request or not.
         try {
            httpServletRequest.getAsyncContext().addListener(new AsyncListener() {
               @Override
               public void onComplete(AsyncEvent event) throws IOException {
                  LOG.debug("onComplete {}", event);
                  decorateResponse(responseWrapper);
               }
    
               @Override
               public void onTimeout(AsyncEvent event) throws IOException {
                  LOG.debug("onTimeout {}", event);
               }
    
               @Override
               public void onError(AsyncEvent event) throws IOException {
                  LOG.debug("onError {}", event);
               }
    
               @Override
               public void onStartAsync(AsyncEvent event) throws IOException {
                  LOG.debug("onStartAsync {}", event);
                  event.getAsyncContext().addListener(this);
               }
            }
            , httpServletRequest, responseWrapper);
            LOG.debug("After chain.doFilter, async was started");
         } catch (IllegalStateException e) {
            LOG.debug("Async not active it appears... {}", e.getMessage());
            decorateResponse(responseWrapper);
         }
      }
    

    【讨论】:

      【解决方案2】:

      这个答案有两个部分。

      1) chain.doFilter(request, response); 仍然是必需的。

      2) 这不起作用的原因是,在每个过滤器和 servlet 中,我调用了 request.startAsync(),它启动了一个 new 异步进程,而不是使用现有的进程。因此,如果过滤器启动了一个异步进程,并且 servlet 也启动了一个,它将覆盖/忽略在过滤器中启动的那个。要解决此问题,您必须通过调用request.isAsyncStarted() 检查是否已经启动了异步进程,如果是,则不应启动新的异步上下文,而应使用request.getAsyncContext() 获取现有的。下面是我为每个 servlet 和过滤器创建的帮助器类,这样我就可以调用 AsyncHelper.getAsyncContext(request, response),它会检索现有的 AsyncContext,或者创建一个新的。

      public class AsyncHelper {
          public static AsyncContext getAsyncContext(ServletRequest request, ServletResponse response) {
              AsyncContext asyncContext = null;
              if (request.isAsyncStarted()) {
                  asyncContext = request.getAsyncContext();
              }
              else {
                  asyncContext = request.startAsync(request, response);
                  asyncContext.setTimeout(2000);
              }
              return asyncContext;
          }
      }
      

      【讨论】:

      • 阅读 AsyncContext.isAsyncStarted 的 api-docs 看起来这个例子很容易导致 IllegalStateException,这取决于 servlet 正在做什么;在对AsyncContext.isAsyncStartedAsyncContext.getAsyncContext 的调用之间,另一个线程可能通过调用AsyncContext.completedAsyncContext.dispatch 改变了AsyncContext 的状态
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 1970-01-01
      相关资源
      最近更新 更多