springboot异常处理之404
源码分析
在springboot中默认有一个异常处理器接口ErrorContorller,该接口提供了getErrorPath()方法,此接口的BasicErrorController实现类实现了getErrorPath()方法,如下:
/** AbstractErrorController是ErrorContoller的实现类*/@Controller@RequestMapping("${server.error.path:${error.path:/error}}")public class BasicErrorController extends AbstractErrorController {private final ErrorProperties errorProperties;...@Overridepublic 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);}@RequestMappingpublic 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类定义如下:
public class ErrorProperties {/*** Path of the error controller.*/@Value("${error.path:/error}")private String path = "/error";...}
由此可见,springboot中默认有一个处理/error映射的控制器,有error和errorHtml两个方法的存在,它可以处理来自浏览器页面和来自机器客户端(app应用)的请求。
当用户请求不存在的url时,dispatcherServlet会交由ResourceHttpRequestHandler映射处理器来处理该请求,并在handlerRequest方法中,重定向至/error映射,代码如下:
@Overridepublic void handleRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// For very general mappings (e.g. "/") we need to check 404 firstResource resource = getResource(request);if (resource == null) {logger.debug("Resource not found");response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404return;}...}
getResource(request)会调用this.resolverChain.resolveResource(request, path, getLocations())方法,getLocations()方法返回结果如下:
接着程序会执行到getResource(pathToUse, location)方法如下:
@Nullableprotected Resource getResource(String resourcePath, Resource location) throws IOException {// 新建一个resource对象,url为 location + resourcePath,判断此对象(文件)是否存在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()中,程序会在locations目录中寻找path目录下文件,由于不存在,返回null。
最终也就导致程序重定向至/error映射,如果是来自浏览器的请求,也就会返回/template/error/404.html页面,所以对于404请求,只需要在template目录下新建error目录,放入404页面即可。
使用注意
1.在springboot4.x中我们可以自定义ControllerAdvice注解 + ExceptionHandler注解来助理不同错误类型的异常,但在springboot中404异常和拦截器异常由spring自己处理。