原始碼:
@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