上一篇文章说到,参数校验,每每须要和全局的异常拦截器来配套使用,使得返回的数据结构永远是保持一致的。参数异常springboot默认的返回结构:前端
{
"timestamp": "2019-04-25T13:09:02.196+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"Pattern.param.birthday",
"Pattern.birthday",
"Pattern.java.lang.String",
"Pattern"
],
"arguments": [
{
"codes": [
"param.birthday",
"birthday"
],
"arguments": null,
"defaultMessage": "birthday",
"code": "birthday"
},
[],
{
"defaultMessage": "\d{4}-\d{2}-\d{2}",
"codes": [
"\d{4}-\d{2}-\d{2}"
],
"arguments": null
}
],
"defaultMessage": "须要匹配正则表达式"\d{4}-\d{2}-\d{2}"",
"objectName": "param",
"field": "birthday",
"rejectedValue": "apple",
"bindingFailure": false,
"code": "Pattern"
}
],
"message": "Validation failed for object='param'. Error count: 1",
"path": "/validate/notblank"
}
复制代码
无论是正常的状况,仍是异常的状况,对于前端(或者app)来讲,最好返回值的结构都是一致的,这样才方便解释。java
public class BaseResult {
private int code;
private String message;
private Object data;
// 省略getter setter方法,全参构造方法
}
复制代码
无论什么接口,都采用这样的数据结构返回给前端。好比约定code为0时是成功,其余错误定义出具体的错误码,message放错误信息,data对象放相应的数据。web
@RestControllerAdvice
public class GlobalExceptionHandlerAdvice {
}
复制代码
使用RestControllerAdvice能够标识一个类为异常捕获类。正则表达式
经过参数异常的测试,能够知道参数有异常时会抛出org.springframework.web.bind.MethodArgumentNotValidException。咱们如今手动捕获 这个异常,而且返回一个BaseResult格式的响应。spring
@ExceptionHandler(MethodArgumentNotValidException.class)
public BaseResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
FieldError fieldError = result.getFieldError();
String defaultMessage = fieldError.getDefaultMessage();
return new BaseResult(11000, defaultMessage, null);
}
复制代码
使用ExceptionHandler能够捕获异常类型。这里捕获了参数错误会抛出的异常,而后返回了自定义的结果。这里错误码为随便填写,真实开发,建议定义一个错误码枚举类。springboot
效果以下:bash
返回的结果就比较友好了,前端处理起来也方便。数据结构
使用异常来处理业务逻辑,会使代码写起来更加流畅。好比说,一个删除用户数据的方法,返回值为void(无返回值),可是当传入的用户id不存在的时候,就应该返回一个用户不存在的结果,这对于void返回值的方法来讲,显得无能为力。可是,使用异常流来处理该业务逻辑,会变得很是简单。咱们直接抛出一个自定义异常,而后在异常捕获器上捕获该异常,再把结果返回给前端便可。app
public class WebException extends RuntimeException {
private int code;
private String errorMsg;
public WebException(int code, String errorMsg) {
super(errorMsg);
this.code = code;
this.errorMsg = errorMsg;
}
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
// 省略getter setter方法
}
复制代码
这里定义了一个运行时异常,重写了fillInStackTrace方法,重写该方法是不保留异常信息栈。由于咱们使用该异常来处理业务逻辑,都是咱们手动抛出的,因此也不须要保存异常信息栈了,这会提高性能。ide
@ExceptionHandler(WebException.class)
public BaseResult handleWebException(WebException e) {
return new BaseResult(e.getCode(), e.getErrorMsg(), null);
}
复制代码
在以前的UserController类,修改以前写的deleteUser方法,以下:
@DeleteMapping(value = "/{userId}")
public Object deleteUser(@PathVariable(value = "userId") Integer userId) {
if (userId == 0) {
throw new WebException(-1, "用户不存在");
}
return new BaseResult(1, "成功", null);
}
复制代码
这里定义一个delete请求的接口,接收一个userId参数,若是userId等于0,则返回该用户不存在。测试结果以下:
当userId为0时,提示用户不存在
当userId为1时,提示成功.
这里实现了全局异常捕获,而且介绍了异常流处理业务逻辑。这里只是一个小demo,还有不少待改进的地方。好比说,我没有定义一个错误码枚举类。在定义了错误码枚举类的前提下,修改构造BaseResult的模式,能够采用静态工厂模式来构造等。这里就不展开讨论了。