我想知道如何读取请求主体的滤波器响应,如果@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
}
}
可赎回的问题是,调度器的servlet自身开始异步处理和过滤器实际处理的请求之前退出。
当可赎回到达调度的servlet,它释放所有过滤器释放容器线程池从(过滤器基本完成他们的工作)。当可赎回产生的结果,调度器的servlet与相同的请求再次调用,并且响应immidiately通过从可赎回数据返回满足。这是通过类型的请求属性处理AsyncTaskManager
,其保持关于异步请求的处理的一些信息。这可以通过测试Filter
和HandlerInterceptor
。Filter
只执行一次,但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] 删除。
我来说两句