文章目录
Spring提供多种方法将异常转化为响应:
- 特定的Spring异常会自动映射为指定的HTTP状态码;
- 异常上可以添加
@ResponseStatus
注解,从而将其映射为某一个状态码; - 在方法上可以添加
ExceptionHandler
注解,使其用来处理异常;
将异常映射为HTTP状态码
Spring异常自动映射
Spring异常 | HTTP状态码 |
---|---|
BindException | 400-Bad Request |
ConversionNotSupportedException | 500-Internal Error |
HttpMediaTypeNotAcceptableException | 406-Not Acceptable |
HttpMediaTypeNotSupportedException | 415-Unsupported Media Type |
HttpMessageNotReadableException | 400-Bad Request |
HttpMessageNotWritableException | 500-Internal Server Error |
HttpRequestMethodNotSupportedException | 405-Method Not Allow |
MethodArgumentNotValidException | 400-Bad Request |
MissingServletRequestParameterException | 400-Bad Request |
MissingServletRequestPartException | 400-Bad Request |
NoSuchRequestHandlingMethodException | 404-Not Found |
TypeMismatchException | 400-Bad Request |
上述异常由Spring自身抛出,作为DispatcherServlet处理过程中或执行校验时出现问题的结果。
@ResponseStatus
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "service not found")
public class MyException extends RuntimeException{
}
@RestController
public class ExceptionController {
@GetMapping("exception")
private void exceptionTest(@RequestParam String param){
if("no".equals(param)){
throw new MyException();
}
}
}
上述代码中使用@ResponseStatus
注解,在程序抛出MyException时,指定响应具有404状态码。
@ExceptionHandler
处理异常希望响应中不仅包含状态码,还需要包含产生的错误,仅将异常映射为HTTP状态码就不够了。此时,需要按照处理请求的方式来处理异常。
@PostMapping("exception/handler")
public String exceptionHandler(@Nullable FileMode fileMode){
try {
fileMode.getName().toLowerCase();
} catch (Exception e) {
return "false";
}
return fileMode.getName();
}
上述代码中,异常处理代码和业务逻辑代码混在了一起。我们可以利用@ExceptionHandler
将异常处理逻辑抽取出来。
@PostMapping("exception/handler")
public String exceptionHandler(@Nullable FileMode fileMode){
fileMode.getName().toLowerCase();
return fileMode.getName();
}
@ExceptionHandler(Exception.class)
public String handlerException(){
return "false";
}
将业务逻辑和异常处理分开,代码结构更加清晰。
⚠️:@ExceptionHandler标注的方法可以处理同一个控制器中
所有的处理器方法抛出的异常;若要处理所有控制器方法抛出的异常,需要将@ExceptionHandler标注的方法定义到控制器通知类
中。
为控制器添加通知(@ControllerAdvice)
控制器通知类使用@ControllerAdvice
标注,会包含一个或多个如下类型方法:
- @ExceptionHandler注解标注的方法:
全局处理控制器中异常。 - @InitBinder注解标注的方法:
设置WebDataBinder,用来自动绑定前台请求参数到Model中。 - @ModelAttribute注解标注的方法:
绑定键值对到Model里,此处是让全局的@RequestMapping都能获取在此处设置的键值对。
在通知类中,上述方法会运用到所有controller中带有@RequestMapping的方法上。
@ControllerAdvice本身带有@Component。
使用样例如下,很好地体现了通知类中各个注解的作用:
@ControllerAdvice
public class GlobleControllerAdvice {
//@ExceptionHandler参数中指定处理的异常或在方法参数中指定
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(Exception e, WebRequest request){
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("errorMessage", e.getMessage());
return modelAndView;
}
/**
* 在执行控制器之前执行,初始化数据模型,键值对可以用ModelMap接收
* @param model
*/
@ModelAttribute
public void addAttribute(Model model){
model.addAttribute("globleModelAttr", "gma");
}
/**
* 控制器参数转换之前被执行的代码
* @param webDataBinder Spring MVC会自动生成的参数
*/
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
//自定义日期编辑器
CustomDateEditor dateEditor = new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false);
webDataBinder.registerCustomEditor(Date.class, dateEditor);
}
}
上述代码为一个ControllerAdvice示例,调用代码如下:
@RestController
public class ExceptionController {
@GetMapping("/exception")
public ModelAndView testExceptionHandler(Date date, ModelMap modelMap){
System.out.println(modelMap.get("globleModelAttr"));
System.out.println(date);
throw new NullPointerException("yanzy test error message");
}
}
错误页面定义:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>错误页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<!-- jquery -->
<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
</head>
<body>
<h1 th:text="*{errorMessage}"></h1>
<h1 th:text="*{globleModelAttr}"></h1>
</body>
</html>
请求此服务,仅带有参数"?date=2019-05-08",响应结果为:
<!DOCTYPE HTML>
<html>
<head>
<title>错误页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<!-- jquery -->
<script type="text/javascript" src="/js/jquery.min.js"></script>
</head>
<body>
<h1>yanzy test error message</h1>
<h1></h1>
</body>
</html>
控制台打印信息:
gma
Wed May 08 00:00:00 CST 2019
多个@ExceptionHandler共存时优先级
根据声明的异常匹配度进行优先级排序。
注解注释的方法可以包含下列参数:
返回值可以为下列类型:
⚠️:advice中的@ExceptionHandler优先级低于当前Controller中定义的@ExceptionHandler