作者: BNDong
链接: https://www.cnblogs.com/bndong/p/10135370.html
前言
在启动应用时会发现在控制台打印的日志中出现了两个路径为 {[/error]} 的访问地址,当系统中发送异常错误时,Spring Boot 会根据请求方式分别跳转到以 JSON 格式或以界面显示的 /error 地址中显示错误信息。- 2018-12-18 09:36:24.627 INFO 19040 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" ...
- 2018-12-18 09:36:24.632 INFO 19040 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" ...
复制代码 Spring Boot 基础就不介绍了,推荐下这个实战教程:
https://github.com/javastacks/spring-boot-best-practice
默认异常处理
使用 AJAX 方式请求时返回的 JSON 格式错误信息。- {
- "timestamp": "2018-12-18T01:50:51.196+0000",
- "status": 404,
- "error": "Not Found",
- "message": "No handler found for GET /err404",
- "path": "/err404"
- }
复制代码 使用浏览器请求时返回的错误信息界面。
自定义异常处理
引入依赖
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.2.54</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-freemarker</artifactId>
- </dependency>
复制代码 fastjson 是 JSON 序列化依赖, spring-boot-starter-freemarker 是一个模板引擎,用于我们设置错误输出模板。
增加配置
- # 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)
- spring.mvc.throw-exception-if-no-handler-found=true
- # 不要为工程中的资源文件建立映射
- spring.resources.add-mappings=false
复制代码- spring:
- # 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)
- mvc:
- throw-exception-if-no-handler-found: true
- # 不要为工程中的资源文件建立映射
- resources:
- add-mappings: false
复制代码 Spring Boot 基础就不介绍了,推荐下这个实战教程:
https://github.com/javastacks/spring-boot-best-practice
新建错误信息实体
- /**
- * 信息实体
- */
- public class ExceptionEntity implements Serializable {
- private static final long serialVersionUID = 1L;
- private String message;
- private int code;
- private String error;
- private String path;
- @JSONField(format = "yyyy-MM-dd hh:mm:ss")
- private Date timestamp = new Date();
- public static long getSerialVersionUID() {
- return serialVersionUID;
- }
- public String getMessage() {
- return message;
- }
- public void setMessage(String message) {
- this.message = message;
- }
- public int getCode() {
- return code;
- }
- public void setCode(int code) {
- this.code = code;
- }
- public String getError() {
- return error;
- }
- public void setError(String error) {
- this.error = error;
- }
- public String getPath() {
- return path;
- }
- public void setPath(String path) {
- this.path = path;
- }
- public Date getTimestamp() {
- return timestamp;
- }
- public void setTimestamp(Date timestamp) {
- this.timestamp = timestamp;
- }
- }
复制代码 新建自定义异常
- /**
- * 自定义异常
- */
- public class BasicException extends RuntimeException {
- private static final long serialVersionUID = 1L;
- private int code = 0;
- public BasicException(int code, String message) {
- super(message);
- this.code = code;
- }
- public int getCode() {
- return this.code;
- }
- }
复制代码- /**
- * 业务异常
- */
- public class BusinessException extends BasicException {
- private static final long serialVersionUID = 1L;
- public BusinessException(int code, String message) {
- super(code, message);
- }
- }
复制代码 BasicException 继承了 RuntimeException ,并在原有的 Message 基础上增加了错误码 code 的内容。而 BusinessException 则是在业务中具体使用的自定义异常类,起到了对不同的异常信息进行分类的作用。
新建 error.ftl 模板文件
位置:/src/main/resources/templates/ 用于显示错误信息- <!DOCTYPE html>
- <html>
- <head>
- <meta name="robots" content="noindex,nofollow" />
- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
-
- </head>
- <body>
- <h2>Exception Datas</h2>
- <table>
- <tbody>
- <tr>
- <td>Code</td>
- <td>
- ${(exception.code)!}
- </td>
- </tr>
- <tr>
- <td>Time</td>
- <td>
- ${(exception.timestamp?datetime)!}
- </td>
- </tr>
- <tr>
- <td>Path</td>
- <td>
- ${(exception.path)!}
- </td>
- </tr>
- <tr>
- <td>Exception</td>
- <td>
- ${(exception.error)!}
- </td>
- </tr>
- <tr>
- <td>Message</td>
- <td>
- ${(exception.message)!}
- </td>
- </tr>
- </tbody>
- </table>
- </body>
- </html>
复制代码 编写全局异常控制类
- /**
- * 全局异常控制类
- */
- @ControllerAdvice
- public class GlobalExceptionHandler {
- /**
- * 404异常处理
- */
- @ExceptionHandler(value = NoHandlerFoundException.class)
- @ResponseStatus(HttpStatus.NOT_FOUND)
- public ModelAndView errorHandler(HttpServletRequest request, NoHandlerFoundException exception, HttpServletResponse response) {
- return commonHandler(request, response,
- exception.getClass().getSimpleName(),
- HttpStatus.NOT_FOUND.value(),
- exception.getMessage());
- }
- /**
- * 405异常处理
- */
- @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
- public ModelAndView errorHandler(HttpServletRequest request, HttpRequestMethodNotSupportedException exception, HttpServletResponse response) {
- return commonHandler(request, response,
- exception.getClass().getSimpleName(),
- HttpStatus.METHOD_NOT_ALLOWED.value(),
- exception.getMessage());
- }
- /**
- * 415异常处理
- */
- @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
- public ModelAndView errorHandler(HttpServletRequest request, HttpMediaTypeNotSupportedException exception, HttpServletResponse response) {
- return commonHandler(request, response,
- exception.getClass().getSimpleName(),
- HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(),
- exception.getMessage());
- }
- /**
- * 500异常处理
- */
- @ExceptionHandler(value = Exception.class)
- public ModelAndView errorHandler (HttpServletRequest request, Exception exception, HttpServletResponse response) {
- return commonHandler(request, response,
- exception.getClass().getSimpleName(),
- HttpStatus.INTERNAL_SERVER_ERROR.value(),
- exception.getMessage());
- }
- /**
- * 业务异常处理
- */
- @ExceptionHandler(value = BasicException.class)
- private ModelAndView errorHandler (HttpServletRequest request, BasicException exception, HttpServletResponse response) {
- return commonHandler(request, response,
- exception.getClass().getSimpleName(),
- exception.getCode(),
- exception.getMessage());
- }
- /**
- * 表单验证异常处理
- */
- @ExceptionHandler(value = BindException.class)
- @ResponseBody
- public ExceptionEntity validExceptionHandler(BindException exception, HttpServletRequest request, HttpServletResponse response) {
- List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
- Map<String,String> errors = new HashMap<>();
- for (FieldError error:fieldErrors) {
- errors.put(error.getField(), error.getDefaultMessage());
- }
- ExceptionEntity entity = new ExceptionEntity();
- entity.setMessage(JSON.toJSONString(errors));
- entity.setPath(request.getRequestURI());
- entity.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
- entity.setError(exception.getClass().getSimpleName());
- response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
- return entity;
- }
- /**
- * 异常处理数据处理
- */
- private ModelAndView commonHandler (HttpServletRequest request, HttpServletResponse response,
- String error, int httpCode, String message) {
- ExceptionEntity entity = new ExceptionEntity();
- entity.setPath(request.getRequestURI());
- entity.setError(error);
- entity.setCode(httpCode);
- entity.setMessage(message);
- return determineOutput(request, response, entity);
- }
- /**
- * 异常输出处理
- */
- private ModelAndView determineOutput(HttpServletRequest request, HttpServletResponse response, ExceptionEntity entity) {
- if (!(
- request.getHeader("accept").contains("application/json")
- || (request.getHeader("X-Requested-With") != null && request.getHeader("X-Requested-With").contains("XMLHttpRequest"))
- )) {
- ModelAndView modelAndView = new ModelAndView("error");
- modelAndView.addObject("exception", entity);
- return modelAndView;
- } else {
- response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
- response.setCharacterEncoding("UTF8");
- response.setHeader("Content-Type", "application/json");
- try {
- response.getWriter().write(ResultJsonTools.build(
- ResponseCodeConstant.SYSTEM_ERROR,
- ResponseMessageConstant.APP_EXCEPTION,
- JSONObject.parseObject(JSON.toJSONString(entity))
- ));
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
- }
复制代码 @ControllerAdvice
作用于类上,用于标识该类用于处理全局异常。
@ExceptionHandler
作用于方法上,用于对拦截的异常类型进行处理。value 属性用于指定具体的拦截异常类型,如果有多个 ExceptionHandler 存在,则需要指定不同的 value 类型,由于异常类拥有继承关系,所以 ExceptionHandler 会首先执行在继承树中靠前的异常类型。
BindException
该异常来自于表单验证框架 Hibernate validation,当字段验证未通过时会抛出此异常。
编写测试 Controller
- @RestController
- public class TestController {
- @RequestMapping(value = "err")
- public void error(){
- throw new BusinessException(400, "业务异常错误信息");
- }
- @RequestMapping(value = "err2")
- public void error2(){
- throw new NullPointerException("手动抛出异常信息");
- }
- @RequestMapping(value = "err3")
- public int error3(){
- int a = 10 / 0;
- return a;
- }
- }
复制代码 使用 AJAX 方式请求时返回的 JSON 格式错误信息。- # /err
- {
- "msg": "应用程序异常",
- "code": -1,
- "status_code": 0,
- "data": {
- "path": "/err",
- "code": 400,
- "error": "BusinessException",
- "message": "业务异常错误信息",
- "timestamp": "2018-12-18 11:09:00"
- }
- }
- # /err2
- {
- "msg": "应用程序异常",
- "code": -1,
- "status_code": 0,
- "data": {
- "path": "/err2",
- "code": 500,
- "error": "NullPointerException",
- "message": "手动抛出异常信息",
- "timestamp": "2018-12-18 11:15:15"
- }
- }
- # /err3
- {
- "msg": "应用程序异常",
- "code": -1,
- "status_code": 0,
- "data": {
- "path": "/err3",
- "code": 500,
- "error": "ArithmeticException",
- "message": "/ by zero",
- "timestamp": "2018-12-18 11:15:46"
- }
- }
- # /err404
- {
- "msg": "应用程序异常",
- "code": -1,
- "status_code": 0,
- "data": {
- "path": "/err404",
- "code": 404,
- "error": "NoHandlerFoundException",
- "message": "No handler found for GET /err404",
- "timestamp": "2018-12-18 11:16:11"
- }
- }
复制代码 使用浏览器请求时返回的错误信息界面。
示例代码:https://github.com/BNDong/spring-cloud-examples/tree/master/spring-cloud-zuul/cloud-zuul
参考资料:
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |