ps: 推薦一下本人的通用后臺(tái)管理項(xiàng)目spring-mybatis-admin
源碼分析
在springboot中默認(rèn)有一個(gè)異常處理器接口ErrorContorller,該接口提供了getErrorPath()方法,此接口的BasicErrorController實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)了getErrorPath()方法,如下:
/*
* AbstractErrorController是ErrorContoller的實(shí)現(xiàn)類(lèi)
*/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
...
@Override
public String getErrorPath() {
return this.errorProperties.getPath();
}
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
....
}
errorProperties類(lèi)定義如下:
public class ErrorProperties {
/**
* Path of the error controller.
*/
@Value("${error.path:/error}")
private String path = "/error";
...
}
由此可見(jiàn),springboot中默認(rèn)有一個(gè)處理/error映射的控制器,有error和errorHtml兩個(gè)方法的存在,它可以處理來(lái)自瀏覽器頁(yè)面和來(lái)自機(jī)器客戶(hù)端(app應(yīng)用)的請(qǐng)求。
當(dāng)用戶(hù)請(qǐng)求不存在的url時(shí),dispatcherServlet會(huì)交由ResourceHttpRequestHandler映射處理器來(lái)處理該請(qǐng)求,并在handlerRequest方法中,重定向至/error映射,代碼如下:
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 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); // 404
return;
}
...
}
getResource(request)會(huì)調(diào)用this.resolverChain.resolveResource(request, path, getLocations())方法,getLocations()方法返回結(jié)果如下:
接著程序會(huì)執(zhí)行到getResource(pathToUse, location)方法如下:
@Nullable
protected Resource getResource(String resourcePath, Resource location) throws IOException {
// 新建一個(gè)resource對(duì)象,url為 location + resourcePath,判斷此對(duì)象(文件)是否存在
Resource resource = location.createRelative(resourcePath);
if (resource.isReadable()) {
if (checkResource(resource, location)) {
return resource;
}
else if (logger.isWarnEnabled()) {
Resource[] allowedLocations = getAllowedLocations();
logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " +
"but resource \"" + resource.getURL() + "\" is neither under the " +
"current location \"" + location.getURL() + "\" nor under any of the " +
"allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]"));
}
}
return null;
}
在resource.isReadable()中,程序會(huì)在locations目錄中尋找path目錄下文件,由于不存在,返回null。
最終也就導(dǎo)致程序重定向至/error映射,如果是來(lái)自瀏覽器的請(qǐng)求,也就會(huì)返回/template/error/404.html頁(yè)面,所以對(duì)于404請(qǐng)求,只需要在template目錄下新建error目錄,放入404頁(yè)面即可。
使用注意
- 在springboot4.x中我們可以自定義
ControllerAdvice注解 +ExceptionHandler注解來(lái)處理不同錯(cuò)誤類(lèi)型的異常,但在springboot中404異常和攔截器異常由spring自己處理。