Spring MVC中的异常
首先了解下 Spring MVC中定义的一些异常。
异常 | 说明 |
---|---|
HttpRequestMethodNotSupportedException | 请求处理程序不支持具体请求方法 |
HttpMediaTypeNotSupportedException | MediaType不支持 |
HttpMediaTypeNotAcceptableException | 当请求处理程序无法生成客户端可接受的MediaTyp时引发异常。 |
MissingPathVariableException | 方法在从URL提取的URI变量中不存在 |
MissingServletRequestParameterException | 指示缺少参数 |
ServletRequestBindingException | 扩展ServletException |
ConversionNotSupportedException | 当找不到适用于bean属性的编辑器或转换器时引发异常。 |
TypeMismatchException | 尝试设置bean属性时,类型不匹配引发异常。 |
HttpMessageNotReadableException | 消息转换器read 时失败 |
HttpMessageNotWritableException | 消息转换器write 时失败 |
MethodArgumentNotValidException | 对带有{@code@Valid}注释的参数进行验证失败时引发的异常 |
MissingServletRequestPartException | multipart/form-data请求时,找不到对应名称 |
BindException | 绑定错误 |
NoHandlerFoundException | 当DispatcherServlet找不到请求的处理程序时,它会发送404 |
AsyncRequestTimeoutException | 异步请求超时异常 |
1. 指定4xx、5xx错误页面
思路
spring boot中的DefaultErrorViewResolver解析器负责对错误视图进行处理,不同的响应状态码,比如500异常,会解析为error/500的视图名。
然后使用模板处理器TemplateAvailabilityProvider,当存在对应模板引擎的视图页面时,会直接跳转到对应视图。
那么,我们只需要引入模板引擎,然后在对应的路径添加页面就可以实现跳转到指定的错误页面了。
案例
1、 添加模板引擎thymeleaf;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
1、 在resources/templates/error目录下添加错误页面;
比如500错误页面,演示效果所以很简单。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>500</title>
</head>
<body>
<h1>服务器500异常</h1>
<ul>
<li>异常发生的时间: [[${timestamp}]]</li>
<li>异常路径: [[${path}]]</li>
<li>错误信息: [[${error}]]</li>
</ul>
</body>
</html>
1、 直接访问异常接口,发现返回了我们指定的页面;
2. 全局异常捕获
注解介绍
全局异常捕获涉及到以下几个重要的注解。
@ControllerAdvice
是spring-web-5.3.8.jar包提供的一个注解,在AOP中,Advice是增强的意思,所以@ControllerAdvice可以理解为一个增强@Controller注解,可用于控制器异常统一处理。
@ExceptionHandler
也是spring-web-5.3.8.jar包提供的一个注解,意为异常处理器,可以标注在@Controller类的方法上,当这个@Controller类某个方法发生异常时,被@ExceptionHandler标注的方法会执行。这个注解只有一个属性value,配置项为异常的class名,当发生异常会先进行匹配,value有这个异常的@ExceptionHandler会执行,没有再往父类上找。
@ResponseStatus
的作用就是为了改变HTTP响应的状态码,可以在@ExceptionHandler标注的方法上设置不同异常的响应状态码。
使用案例
定义一个@ControllerAdvice注解标识的类,配置异常处理器即可。
@Slf4j
@ControllerAdvice
public class ExceptionControllerAdvice {
// 最后异常处理器,没被匹配到的Exception异常都由这里处理
@ResponseBody
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String,Object> errorHandler(Exception ex) {
ex.printStackTrace();
return getExceptionModel(500,ex.getMessage(),ex.getClass().getName(),Arrays.toString(ex.getStackTrace()));
}
// 处理NoHandlerFoundException Exception
@ResponseBody
@ExceptionHandler(value = NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Map<String,Object> errorHandler(NoHandlerFoundException ex) {
ex.printStackTrace();
return getExceptionModel(404,ex.getMessage(),ex.getClass().getName(),Arrays.toString(ex.getStackTrace()));
}
// 处理ArithmeticException 返回异常页面
@ExceptionHandler(value = ArithmeticException.class)
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public ModelAndView errorHandler(ArithmeticException ex) {
ex.printStackTrace();
return new ModelAndView("error/500");
}
// 获取异常中的信息封装为MAP
private Map<String,Object> getExceptionModel(int code,String msg,String execName,String stackTrace){
Map<String,Object> map = new HashMap<>();
map.put("code", code);
map.put("msg",msg);
map.put("execName",execName);
map.put("stackTrace", stackTrace);
return map;
}
}
注意事项
1、 可以使用@RestControllerAdvice,是@ControllerAdvice和@ResponseBody的组合注解,表示整个类所有方法都返回Json格式;
2、 404异常是不能被@ControllerAdvice捕获的,因为有很多静态资源的映射器一直存在,获取的时候也会获取到它们,只会在执行的时候设置response为404,想要抛出异常,需要特殊配置;
spring:
mvc:
servlet:
load-on-startup: 1
# 抛出找不到映射器异常
throw-exception-if-no-handler-found: true
# 设置静态资源映射目录
static-path-pattern: /statics/**
1、 返回值可以返回Json,或者ModelView;
2、 并不是所有异常都能捕获,比如Security中的很多异常都是自己的异常过滤器处理掉了;
3、 某些框架会对异常进行层层包装,这时只会捕获到最外层的异常;
3. 重写BasicErrorController
可以在ErrorMvcAutoConfiguration自动配置类中,可以看到注入了BasicErrorController异常访问控制器,我们可以覆盖这个Bean,来进行自定义处理。
@Bean
@ConditionalOnMissingBean(
value = {
ErrorController.class},
search = SearchStrategy.CURRENT
)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(), (List)errorViewResolvers.orderedStream().collect(Collectors.toList()));
}
我们可以自定义这两个方法的处理,比如自定义Json格式,还可以将request中的更多错误信息返回。具体怎么做,可以自己去实现,这里就给出思路了。。。
在实际开发怎么处理
在实际开发中,可以为几种情况。
1、 前后端不分离,这种页面和后台在一起的项目,可以重写BasicErrorController,定义自己风格的错误页面;
2、 前后分离,这种一般都是JSON交互数据,后端不可能还去搞一些页面资源,所以后端直接使用全局异常捕获都返回JSON信息,前端根据不同的状态码去跳转页面即可;
版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: