SpringBoot 统一异常管理

通常,SpringBoot 默认的异常页面是这样的,用户根本看不懂,系统遇到 Bug 的话,也不知道跟管理员怎么反馈。   而自定义的异常页面是这样的,意思明朗很多。     通过配置全局异常管理,我们不仅可以美化页面的显示,自定义错误的信息。还可以捕获很多我们遇到的错误,并将它们写入日志中。虽然很多提交表单的异常可以通过前台 js 避免,但是万一用户禁用 js 呢?为了数据安全,我们在后台还是要加一层安全保护。   现在就开始吧!

代码如下

1、这是一个统一异常处理类 GlobalExceptionHandler.java
  1. package com.liuyanzhao.forum.exception;
  2. import org.hibernate.service.spi.ServiceException;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.http.converter.HttpMessageNotReadableException;
  7. import org.springframework.ui.Model;
  8. import org.springframework.validation.BindException;
  9. import org.springframework.validation.BindingResult;
  10. import org.springframework.validation.FieldError;
  11. import org.springframework.web.HttpMediaTypeNotSupportedException;
  12. import org.springframework.web.HttpRequestMethodNotSupportedException;
  13. import org.springframework.web.bind.MethodArgumentNotValidException;
  14. import org.springframework.web.bind.MissingServletRequestParameterException;
  15. import org.springframework.web.bind.annotation.ControllerAdvice;
  16. import org.springframework.web.bind.annotation.ExceptionHandler;
  17. import org.springframework.web.bind.annotation.ResponseStatus;
  18. import org.springframework.web.servlet.NoHandlerFoundException;
  19. import javax.validation.ConstraintViolation;
  20. import javax.validation.ConstraintViolationException;
  21. import javax.validation.ValidationException;
  22. import java.util.Set;
  23. /**
  24.  * @author 言曌
  25.  * @date 2018/3/20 下午12:57
  26.  */
  27. @ControllerAdvice
  28. public class GlobalExceptionHandler {
  29.     // 日志记录工具
  30.     private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
  31.     //错误显示页面,即 /public/error/error.html 文件
  32.     public static final String viewName = "/error/error";
  33.     /**
  34.      * 400 - Bad Request
  35.      */
  36.     @ResponseStatus(HttpStatus.BAD_REQUEST)
  37.     @ExceptionHandler(MissingServletRequestParameterException.class)
  38.     public String handleMissingServletRequestParameterException(MissingServletRequestParameterException e, Model model) {
  39.         logger.error("缺少请求参数", e);
  40.         String message = "【缺少请求参数】" + e.getMessage();
  41.         model.addAttribute("message", message);
  42.         model.addAttribute("code"400);
  43.         return viewName;
  44.     }
  45.     /**
  46.      * 400 - Bad Request
  47.      */
  48.     @ResponseStatus(HttpStatus.BAD_REQUEST)
  49.     @ExceptionHandler(HttpMessageNotReadableException.class)
  50.     public String handleHttpMessageNotReadableException(HttpMessageNotReadableException e, Model model) {
  51.         logger.error("参数解析失败", e);
  52.         String message = "【参数解析失败】" + e.getMessage();
  53.         model.addAttribute("message", message);
  54.         model.addAttribute("code"400);
  55.         return viewName;
  56.     }
  57.     /**
  58.      * 400 - Bad Request
  59.      */
  60.     @ResponseStatus(HttpStatus.BAD_REQUEST)
  61.     @ExceptionHandler(MethodArgumentNotValidException.class)
  62.     public String handleMethodArgumentNotValidException(MethodArgumentNotValidException e, Model model) {
  63.         logger.error("参数验证失败", e);
  64.         BindingResult result = e.getBindingResult();
  65.         FieldError error = result.getFieldError();
  66.         String field = error.getField();
  67.         String code = error.getDefaultMessage();
  68.         String message = "【参数验证失败】" + String.format("%s:%s", field, code);
  69.         model.addAttribute("message", message);
  70.         model.addAttribute("code"400);
  71.         return viewName;
  72.     }
  73.     /**
  74.      * 400 - Bad Request
  75.      */
  76.     @ResponseStatus(HttpStatus.BAD_REQUEST)
  77.     @ExceptionHandler(BindException.class)
  78.     public String handleBindException(BindException e, Model model) {
  79.         logger.error("参数绑定失败", e);
  80.         BindingResult result = e.getBindingResult();
  81.         FieldError error = result.getFieldError();
  82.         String field = error.getField();
  83.         String code = error.getDefaultMessage();
  84.         String message = "【参数绑定失败】" + String.format("%s:%s", field, code);
  85.         model.addAttribute("message", message);
  86.         model.addAttribute("code"400);
  87.         return viewName;
  88.     }
  89.     /**
  90.      * 400 - Bad Request
  91.      */
  92.     @ResponseStatus(HttpStatus.BAD_REQUEST)
  93.     @ExceptionHandler(ConstraintViolationException.class)
  94.     public String handleServiceException(ConstraintViolationException e, Model model) {
  95.         logger.error("参数验证失败", e);
  96.         Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
  97.         ConstraintViolation<?> violation = violations.iterator().next();
  98.         String message = "【参数验证失败】" + violation.getMessage();
  99.         model.addAttribute("message", message);
  100.         model.addAttribute("code"400);
  101.         return viewName;
  102.     }
  103.     /**
  104.      * 400 - Bad Request
  105.      */
  106.     @ResponseStatus(HttpStatus.BAD_REQUEST)
  107.     @ExceptionHandler(ValidationException.class)
  108.     public String handleValidationException(ValidationException e, Model model) {
  109.         logger.error("参数验证失败", e);
  110.         String message = "【参数验证失败】" + e.getMessage();
  111.         model.addAttribute("message", message);
  112.         model.addAttribute("code"400);
  113.         return viewName;
  114.     }
  115.     /**
  116.      * 404 - Not Found
  117.      */
  118.     @ResponseStatus(HttpStatus.NOT_FOUND)
  119.     @ExceptionHandler(NoHandlerFoundException.class)
  120.     public String noHandlerFoundException(NoHandlerFoundException e, Model model) {
  121.         logger.error("Not Found", e);
  122.         String message = "【页面不存在】" + e.getMessage();
  123.         model.addAttribute("message", message);
  124.         model.addAttribute("code"404);
  125.         System.out.println("404404错误");
  126.         return viewName;
  127.     }
  128.     /**
  129.      * 405 - Method Not Allowed
  130.      */
  131.     @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
  132.     @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
  133.     public String handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, Model model) {
  134.         logger.error("不支持当前请求方法", e);
  135.         String message = "【不支持当前请求方法】" + e.getMessage();
  136.         model.addAttribute("message", message);
  137.         model.addAttribute("code"405);
  138.         return viewName;
  139.     }
  140.     /**
  141.      * 415 - Unsupported Media Type
  142.      */
  143.     @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
  144.     @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
  145.     public String handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e, Model model) {
  146.         logger.error("不支持当前媒体类型", e);
  147.         String message = "【不支持当前媒体类型】" + e.getMessage();
  148.         model.addAttribute("message", message);
  149.         model.addAttribute("code"415);
  150.         return viewName;
  151.     }
  152.     /**
  153.      * 业务层需要自己声明异常的情况
  154.      */
  155.     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  156.     @ExceptionHandler(ServiceException.class)
  157.     public String handleServiceException(ServiceException e, Model model) {
  158.         logger.error("业务逻辑异常", e);
  159.         String message = "【业务逻辑异常】" + e.getMessage();
  160.         model.addAttribute("message", message);
  161.         return viewName;
  162.     }
  163.     /**
  164.      * 获取其它异常。包括500
  165.      *
  166.      * @param e
  167.      * @return
  168.      * @throws Exception
  169.      */
  170.     @ExceptionHandler(value = Exception.class)
  171.     public String defaultErrorHandler(Exception e, Model model) {
  172.         logger.error("Exception", e);
  173.         String message = e.getMessage();
  174.         model.addAttribute("message", message);
  175.         model.addAttribute("code"500);
  176.         return viewName;
  177.     }
  178. }
注意修改 37 行的错误页面地址 不知道为什么 404 页面无法捕获   2、这是一个我们自定义的异常,通常我们可以主动抛它,当然,也可以直接抛 Exception CustomException.java
  1. package com.liuyanzhao.forum.exception;
  2. /**
  3.  * 自定义异常
  4.  * @author 言曌
  5.  * @date 2018/3/20 下午12:50
  6.  */
  7. public class CustomException extends RuntimeException {
  8.     /** serialVersionUID */
  9.     private static final long serialVersionUID = 6417641452178955756L;
  10.     public CustomException() {
  11.         super();
  12.     }
  13.     public CustomException(String message) {
  14.         super(message);
  15.     }
  16.     public CustomException(Throwable cause) {
  17.         super(cause);
  18.     }
  19.     public CustomException(String message, Throwable cause) {
  20.         super(message, cause);
  21.     }
  22. }
  3、在 template 或者资源根目录下新建 error 文件夹 error.html
  1. <div class="error-page">
  2.     ${code=} <span th:text="${code}"></span>
  3.     <h2 class="headline text-red" th:text="${code}"></h2>
  4.     <div class="error-content" style="margin-top: 120px;">
  5.         <h3><i class="fa fa-warning text-red"></i> 天哪! 好像遇到了错误.</h3>
  6.         <p style="font-size: 1.2em;">
  7.             错误信息: <span th:text="${message}" class="text text-danger"></span>
  8.         </p>
  9.         <p><a href="javascript:history.go(-1)">返回上一页</a></p>
  10.         <form class="search-form">
  11.             <div class="input-group">
  12.                 <input type="text" name="search" class="form-control" placeholder="Search">
  13.                 <div class="input-group-btn">
  14.                     <button type="submit" name="submit" class="btn btn-danger btn-flat"><i class="fa fa-search"></i>
  15.                     </button>
  16.                 </div>
  17.             </div>
  18.             <!-- /.input-group -->
  19.         </form>
  20.     </div>
  21. </div>
  22. <!-- /.error-page -->
因为 404 页面好像无法捕获,那么我们可以再新建一个 404.html ,内容和上面差不多,改一下错误码为 404 即可。   4、为了演示使用,这里是 UserServiceImpl.java 里的一个添加新用户的方法
  1. @Transactional(rollbackFor = Exception.class)
  2.    @Override
  3.    public void saveUser(User user) {
  4.        String username = user.getUsername();
  5.        String nickname = user.getNickname();
  6.        String password = user.getPassword();
  7.        String email = user.getEmail();
  8.        //1、非空判断
  9.        if (username == null || nickname == null || password == null || email == null) {
  10.            throw new CustomException("存在必填字段为空");
  11.        } else {
  12.            if (username.trim() == "" || password.trim() == "") {
  13.                throw new CustomException("存在必填字段为空字符串");
  14.            } else if(password.trim().length() < 6) {
  15.                throw new CustomException("密码长度小于6位");
  16.            }
  17.        }
  18.        //2、判断用户名是否合法
  19.        if (username != null && username != "") {
  20.            User user2 = userRepository.findByUsername(username);
  21.            //2.1用户名已存在,不可以注册
  22.            if (user2 != null) {
  23.                throw new CustomException("用户名 " + username + " 已被使用");
  24.            }
  25.        }
  26.        //3、判断Email是否合法
  27.        if (email != null && email != "") {
  28.            User user3 = userRepository.findByEmail(email);
  29.            //1.1用户名已存在,不可以注册
  30.            if (user3 != null) {
  31.                throw new CustomException("Email " + email + "已被使用");
  32.            }
  33.        }
  34.        //4、处理其他字段
  35.        user.setCreateTime(System.currentTimeMillis());
  36.        user.setEncodePassword(user.getPassword()); // 加密密码
  37.        user.setIsBanned("N");//正常状态
  38.        user.setNumberVoteUp(0);//票数
  39.        user.setAvatar(AvatarUtil.getGravatar(email));//头像
  40.        if (user.getNickname() == null) {
  41.            user.setNickname(username);//默认昵称是用户名
  42.        }
  43.        userRepository.save(user);
  44.    }
 

最终效果图

先看几个系统自动抛出的异常 1、页面不存在,400错误   2、编辑用户的时候,修改票数(一个 Integer 类型的字段),当我们填成字符串 421swsw 的时候,400错误   3、某个字段只允许一个字符如用户状态是否禁用Y或N,党输入abcd的时候,无法执行,数据库报异常   然后再看几个我们主动抛出的异常 2、添加用户的时候,设置了用户名字段为唯一性,500错误   3、密码小于 6 位

发表评论

目前评论:1