Spring Cloud之实体校验和统一异常处理

前文当中我们对返回结果进行了统一封装,然而对于不同的异常我们在实际开发中最好也有统一的处理。在spring boot中与默认的异常页面,如果我们要开发rest风格的api,返回的json结果数据,那么异常提示也要是json格式的,返回一个页面的错误异常就不友好了

接下来我看怎么统一处理这些异常信息以及自己定义一些常用的异常

我们采用@ControllerAdvice+@ExceptionHandler方式的全局的异常处理,先定义一个全局的异常类,继承Exception ,里面有code属性,表示请求的响应状态码。

public class GlobalException extends Exception implements java.io.Serializable{
   private int code;
   public void setCode(int code) {
      this.code = code;
   }
   public int getCode() {
      return code;
   }
   public GlobalException(GlobalException e) {
        super(e.getMessage());
        this.code = e.getCode();
    }
   public GlobalException(String message) {
        super(message);
    }
   public GlobalException(String message, int code) {
        super(message);
        this.code = code;
    }
}

在定义一个全局异常处理Handler类,我们这里用到了我前面文章讲到的统一结果返回类ResponseData

@ControllerAdvice //全局异常处理,增强的controller
public class GlobalExceptionHandler {
   private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
   /**
    * 自定义异常处理 一个Controller下多个@ExceptionHandler上的异常类型不能出现一样的,否则运行时抛异常.
    * @param req
    * @param e
    * @return
    * @throws Exception
    */
   @ExceptionHandler(value = GlobalException.class)
    @ResponseBody
    public ResponseData jsonErrorHandler(HttpServletRequest req, GlobalException e) throws Exception {
      logger.error("", e);
      ResponseData r = new ResponseData();
        r.setMessage(e.getMessage());
        r.setCode(e.getCode());
        r.setData(null);
        return r;
    }
   /**
    * 系统异常处理
    * @param req
    * @param e
    * @return
    * @throws Exception
    */
   @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseData defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
      logger.error("", e);
      ResponseData r = new ResponseData();
        r.setMessage(e.getMessage());
        if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
            r.setCode(ResponseCode.NOT_FOUND.getCode());
      } else {
          r.setCode(ResponseCode.SERVER_ERROR_CODE.getCode());
      }
        r.setData(null);
        return r;
    }
}

然后我定义一个具体的异常类来继承GlobalException类,比如参数异常:

public class ParamException extends GlobalException {
   private static final long serialVersionUID = 6021390821349937519L;

   public ParamException(String message) {
      super(message,401);//401为返回状态码
   }
}

下面我们模拟一下参数异常的情况来演示下如何使用自定义的异常类:

@PostMapping("/token")
public ResponseData auth(@ApiParam("APP信息") @RequestBody AuthQuery query) throws Exception {
   if (StringUtils.isBlank(query.getAccessKey()) || StringUtils.isBlank(query.getSecretKey())) {
      throw new ParamException("参数错误");
   }
   //查询库中用户信息
   User user = authService.auth(query);
   JWTUtils jwt = JWTUtils.getInstance(rsaConf.getModulus(),rsaConf.getPrivateExponent(),rsaConf.getPublicExponent());
   return ResponseData.ok(jwt.getToken(user.getId().toString()));
}

如果入参的accessKey或者secretKey为空的话会抛出异常:

{
      "code": 401,
      "message": "参数错误",
      "data": null
}

我们还可以自定义其他的异常,跟这个参数异常一样,比如权限异常、服务异常等。我们说回参数异常,我们实际开发中对参数的约束有很多方面,如飞空、长度、格式、类型等等,这样我们直接在controller中写判断就很繁琐了,下面我改造一下,使用hibernate-validator来帮我解决这个问题,实际上spring boot已经集成了这个组件。我们测试一下:

首先我们在实体类中定义约束:

public class AuthQuery {
   @NotBlank(message = "不能为空")
   private String accessKey;
   @NotBlank(message = "不能为空")
   private String secretKey;
   
   public String getAccessKey() {
      return accessKey;
   }
   public void setAccessKey(String accessKey) {
      this.accessKey = accessKey;
   }
   public String getSecretKey() {
      return secretKey;
   }
   public void setSecretKey(String secretKey) {
      this.secretKey = secretKey;
   }
}

然后在controller中需要加入约束的参数上加入注解@validated即可。

@ApiOperation(value = "获取token1")
@PostMapping("/token")
public ResponseData oauth(@Validated AuthQuery query) throws Exception {
   User user = authService.auth(query);
   if (user == null) {
      return ResponseData.failByParam("认证失败");
   }
   JWTUtils jwt = JWTUtils.getInstance(rsaConf.getModulus(),rsaConf.getPrivateExponent(),rsaConf.getPublicExponent());
   return ResponseData.ok(jwt.getToken(user.getId().toString()));
}

我们看看返回值:

{
  "code": 500,
  "message": "org.springframework.validation.BeanPropertyBindingResult: 2 errors\nField error in object 'authQuery' on field 'secretKey': rejected value [null]; codes [NotBlank.authQuery.secretKey,NotBlank.secretKey,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [authQuery.secretKey,secretKey]; arguments []; default message [secretKey]]; default message [不能为空]\nField error in object 'authQuery' on field 'accessKey': rejected value [null]; codes [NotBlank.authQuery.accessKey,NotBlank.accessKey,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [authQuery.accessKey,accessKey]; arguments []; default message [accessKey]]; default message [不能为空]",
  "data": null
}

我们发现返回值很混乱,我们改造下,在handler类中加入如下方法,单独处理这个异常:

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public ResponseData handler(MethodArgumentNotValidException e){
   //简化异常错误信息
   BindingResult bindingResult = e.getBindingResult();
   ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();
   return ResponseData.failByParam(objectError.getDefaultMessage());
}

在看返回信息:

{

  "code": 400,

  "message": "不能为空",

  "data": null

}

查看作者原文链接:http://www.sucai66.com/article/detail/20200708/27.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值