读取响应主体在异步请求

约翰·斯诺:

我想知道如何读取请求主体的滤波器响应,如果@Controller方法返回Callable接口。

我的过滤器看起来是这样的。响应总是空的。任何解决这个?这是只允许使用AsyncListener?

@Component
public class ResposeBodyXmlValidator extends OncePerRequestFilter {
    private final XmlUtils xmlUtils;
    private final Resource xsdResource;

    public ResposeBodyXmlValidator(
        XmlUtils xmlUtils,
        @Value("classpath:xsd/some.xsd") Resource xsdResource
    ) {
        this.xmlUtils = xmlUtils;
        this.xsdResource = xsdResource;
    }

    @Override
    protected void doFilterInternal(
        HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain
    ) throws ServletException, IOException {
        ContentCachingResponseWrapper response = new ContentCachingResponseWrapper(httpServletResponse);

        doFilter(httpServletRequest, response, filterChain);

        if (MediaType.APPLICATION_XML.getType().equals(response.getContentType())) {
            try {
                xmlUtils.validate(new String(response.getContentAsByteArray(), response.getCharacterEncoding()), xsdResource.getInputStream());
            } catch (IOException | SAXException e) {
                String exceptionString = String.format("Chyba při volání %s\nNevalidní výstupní XML: %s",
                    httpServletRequest.getRemoteAddr(),
                    e.getMessage());
                response.setContentType(MediaType.TEXT_PLAIN_VALUE + "; charset=UTF-8");
                response.setCharacterEncoding(StandardCharsets.UTF_8.name());
                response.getWriter().print(exceptionString);
            }
        }
        response.copyBodyToResponse(); // I found this needs to be added at the end of the filter
    }
}
亚当Ostrožlík:

可赎回的问题是,调度器的servlet自身开始异步处理和过滤器实际处理的请求之前退出。

当可赎回到达调度的servlet,它释放所有过滤器释放容器线程池从(过滤器基本完成他们的工作)。当可赎回产生的结果,调度器的servlet与相同的请求再次调用,并且响应immidiately通过从可赎回数据返回满足。这是通过类型的请求属性处理AsyncTaskManager,其保持关于异步请求的处理的一些信息。这可以通过测试FilterHandlerInterceptorFilter只执行一次,但HandlerInterceptor执行两次(原始请求和赎回后的要求完成了工作)

当你需要阅读的请求和响应,解决方案之一就是重写DispatcherServlet的是这样的:

@Bean
@Primary
public DispatcherServlet dispatcherServlet(WebApplicationContext context) {
    return new DispatcherServlet(context) {
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
            ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
            super.service(requestWrapper, responseWrapper);
            responseWrapper.copyBodyToResponse();
        }
    };
}

这样可以确保您可以读取请求和响应多次。另一件事是添加的HandlerInterceptor像这样(你必须通过一些数据作为请求属性):

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
        Exception {
        Object asyncRequestData = request.getAttribute(LOGGER_FILTER_ATTRIBUTE);
        if (asyncRequestData == null) {
            request.setAttribute(LOGGER_FILTER_ATTRIBUTE, new AsyncRequestData(request));
        }
        return true;
    }

    @Override
    public void afterCompletion(
        HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex
    ) throws Exception {
        Object asyncRequestData = request.getAttribute(LOGGER_FILTER_ATTRIBUTE);
        if (asyncRequestData != null && response instanceof ContentCachingResponseWrapper) {
            log(request, (ContentCachingResponseWrapper) response, (AsyncRequestData) asyncRequestData);
        }
    }

afterCompletion执方法只调用一次之后异步请求已被完全处理。preHandle所以你必须检查你的属性的所有脑干只调用了两次。afterCompletion执行,从呼叫响应已经存在,如果你要替换它,你应该调用response.resetBuffer()

这是一个可能的解决方案,并有可能是更好的方式。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章