springboot异常处理之404
ps: 推荐一下本人的通用后台管理项目crowd-admin 以及newbee-mall增强版,喜欢的话给个star就好
开始这篇博文的时候我们先回顾下spring
异常处理的几种机制
异常处理机制
-
@ExceptionHandle
注解处理异常处理单个controller内部抛出的指定异常
-
@ControllerAdvice+@ExceptionHandler
注解处理异常处理所有controller内部抛出的指定异常
-
自定义
HandlerExceptionResolver
类处理异常全局异常处理
事件经过
先说博主最近遇到的问题,在老的spring项目中用@RestControllerAdvice
注解定义的全局异常处理类是按如下方式来处理404请求的:
/**
* 处理404异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(NoHandlerFoundException.class)
public Object handle404Exception(NoHandlerFoundException e, HttpServletRequest request) {
logger.error(e.getMessage(), e);
if (HttpUtil.isAjax(request)) {
return Response.error("您请求路径不存在,请检查url!");
}
return new ModelAndView("error/404");
}
但是在新的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;
...
@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
类定义如下:
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
映射,代码如下:
@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)
会调用this.resolverChain.resolveResource(request, path, getLocations())
方法,getLocations()
方法返回结果如下:
接着程序会执行到getResource(pathToUse, location)
方法如下:
@Nullable
protected 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页面即可。
使用注意
- 在springboot4.x中我们可以自定义
ControllerAdvice
注解 +ExceptionHandler
注解来处理不同错误类型的异常,但在springboot中404异常和拦截器异常由spring自己处理。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗