源代碼:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String uri = request.getRequestURI();
if(pathMatcher.match("/", uri)) {
System.err.println("跳轉");
response.sendRedirect("/swagger-ui.html");
// return false; // 如果此處不返回false, 則springMvc會繼續對“/”路徑進行處理,就會出現多次返回響應的錯誤。
}
return true;
}
注:此處對“/”路徑的訪問返回404.
DispatcherServlet.doDispatch()中對攔截 器的preHandle進行調用:
// 如果攔截器的PreHandle返回false,則此處直接返回退出方法。
if(!mappedHandler.applyPreHandle(request,response)){
return ;
}
// Actually invoke the handler. 調用處理器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 由此處可知如果攔截器的preHandle方法返回false則不會調用處理器(控制器類的方法)
mappedHandler是一個HandlerExcutionChain對象由HandlerMapping返回,HandlerExcutionChain包含一個Handler(處理器對象)和攔截器數組,通過applyPreHandle(request,response)方法會對攔截器數組中的每一個攔截器的preHandle進行調用。
// HandlerExcutionChain類
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
在本例中出現上述錯誤的原因:
攔截器攔截了對“/”路徑的請求,並且調用response.sendRedirect("/swagger-ui.html")返回了響應。由於攔截器沒有返回false,所以SpringMVC會繼續對“/”路徑進行處理。
在沒有找到“/”對應的處理器時,SpringMVC默認會使用ResourceHttpRequestHandler進行請求處理。ResourceHttpRequestHandler在進行請求處理時會進行404檢查,如果路徑或資源不存在則會調用response.sendError(HttpServletResponse.SC_NOT_FOUND);源碼如下:
// ResourceHttpRequestHandler中進行404檢查 // For very general mappings (e.g. "/") we need to check 404 first Resource resource = getResource(request); if (resource == null) { logger.debug("Resource not found"); response.sendError(HttpServletResponse.SC_NOT_FOUND); return; }由2可知,如果資源不存在就會調用response.sendError(HttpServletResponse.SC_NOT_FOUND);而在攔截器中已經調用response.sendRedirect("/swagger-ui.html")對響應進行了返回,所以就會出現多次返回響應的錯誤。
對於上述問題的解決辦法是在response.sendRedirect("/swagger-ui.html");后返回false,或者將攔截路徑由“/”改為response.sendError(HttpServletResponse.SC_NOT_FOUND);后的路徑(在此處為“/error”)。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String uri = request.getRequestURI();
if(pathMatcher.match("/", uri)) {
response.sendRedirect("/swagger-ui.html");
return false; // 如果此處不返回false, 則springMvc會繼續對“/”路徑進行處理,就會出現多次返回響應的錯誤。
}
return true;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String uri = request.getRequestURI();
if(pathMatcher.match("/error", uri)) {
response.sendRedirect("/swagger-ui.html");
}
return true;
}
下面是SpringMVC處理"/api/test/error"請求時打印的部分日志(/api/test/error沒有對應的處理器,即該路徑不存在,報404錯誤):
注:自定義的攔截器是在映射結束后才執行的。
// 請求路徑
2019-02-20 14:55:57.086 DEBUG 2676 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/api/test/error", parameters={}
// 處理器映射器和請求路徑對應的處理器
2019-02-20 14:55:57.092 DEBUG 2676 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
// 處理結果
2019-02-20 14:55:57.094 DEBUG 2676 --- [nio-8080-exec-1] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2019-02-20 14:55:57.095 DEBUG 2676 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND
下面是SpringMVC處理“/api/test/error1”請求時打印的部分日志(/api/test/error1對應的處理器在org.lwt.controller.RoleController類下的.ErrorTest()方法):
// 請求路徑
2019-02-20 15:13:23.860 DEBUG 2676 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : GET "/api/test/error1", parameters={}
// 處理器映射器和請求路徑對應的處理器
2019-02-20 15:13:23.861 DEBUG 2676 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.lwt.vo.Result<java.lang.String> org.lwt.controller.RoleController.ErrorTest()
攔截器調用
// 處理結果
2019-02-20 15:13:23.865 DEBUG 2676 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : Failed to complete request: org.joda.time.IllegalInstantException: 自己拋出錯誤
2019-02-20 15:13:23.867 ERROR 2676 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.joda.time.IllegalInstantException: 自己拋出錯誤] with root cause
// RoleController類
@RestController
@RequestMapping("/api")
@Api
public class RoleController {
// /api/test/error1對應的處理器
@GetMapping("/test/error1")
public Result<String> ErrorTest(){
throw new IllegalInstantException("自己拋出錯誤");
// return Result.success("多參數傳遞");
}
}
下面是SpringMVC處理“/api/test/error1”請求時打印的部分日志(/api/test/error1對應的處理器在org.lwt.controller.RoleController類下的.ErrorTest()方法):
此處和上一次調用的區別是此次調用處理器沒有報錯:
// 請求路徑
2019-02-20 15:21:31.440 DEBUG 8252 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/api/test/error1", parameters={}
// 處理器映射器和請求路徑對應的處理器
2019-02-20 15:21:31.444 DEBUG 8252 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.lwt.vo.Result<java.lang.String> org.lwt.controller.RoleController.ErrorTest()
攔截器中的uri: /api/test/error1
// 處理結果
2019-02-20 15:21:31.473 DEBUG 8252 --- [nio-8080-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json;q=0.8', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [application/json, application/*+json, application/json, application/*+json]
2019-02-20 15:21:31.473 DEBUG 8252 --- [nio-8080-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Writing [org.lwt.vo.Result@334348d5]
2019-02-20 15:21:31.486 DEBUG 8252 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK
