1 回答

TA貢獻1770條經驗 獲得超3個贊
Callable 的問題在于調度程序 servlet 本身會啟動異步處理,并且過濾器會在實際處理請求之前退出。
當 Callable 到達調度程序 servlet 時,它通過釋放所有過濾器(過濾器基本上完成其工作)來從池中釋放容器線程。當 Callable 產生結果時,會使用相同的請求再次調用調度程序 servlet,并且響應會立即由 Callable 返回的數據完成。這是由類型的 request 屬性處理的,AsyncTaskManager該屬性保存有關異步請求處理的一些信息。這可以用Filter和進行測試HandlerInterceptor。Filter只執行一次但HandlerInterceptor執行了兩次(原始請求和Callable完成工作后的請求)
當您需要讀取請求和響應時,解決方案之一是像這樣重寫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();
}
};
}
這樣您就可以確??梢远啻巫x取請求和響應。另一件事是像這樣添加 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().
這是一種可能的解決方案,并且可能有更好的方法。
添加回答
舉報