关于spring web应用中关于如何使用 Bean Validation _API_和hibernate-validator的文章已经很多,本文就不再重复叙述,今天要介绍的重点是在SpringBoot restful服务中如何根据不同验证错误响应不同的自定义错误码。下面直接上代码。
一、定义restful统一结果返回
阿里java开发手册中定义的一段参考【“对于公司外的 http/api 开放接口必须使用“错误码”; 而应用内部推荐异常抛出;跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法、 “错误码”、 “错误简短信息”。】。因此这里也定义个返回结构。
1public class CommonResult<T> implements Serializable { 2 3 /** 4 * serialVersionUID:. 5 */ 6 private static final long serialVersionUID = -7268040542410707954L; 7 8 /** 9 * 是否成功 10 */ 11 private boolean success = false; 12 13 /** 14 * 返回信息 15 */ 16 private String message; 17 18 /** 19 * 装在数据 20 */ 21 private T data; 22 23 /** 24 * 错误代码 25 */ 26 private String code; 27 28 /** 29 * 默认构造器 30 */ 31 public CommonResult() { 32 33 } 34 35 /** 36 * @param success 是否成功 37 * @param message 返回的消息 38 */ 39 public CommonResult(boolean success, String message) { 40 this.success = success; 41 this.message = message; 42 } 43 44 /** 45 * @param success 是否成功 46 */ 47 public CommonResult(boolean success) { 48 this.success = success; 49 } 50 51 /** 52 * @param code error code 53 * @param message success or error messages 54 */ 55 public CommonResult(String code, String message) { 56 this.code = code; 57 this.message = message; 58 } 59 60 /** 61 * @param success 是否成功 62 * @param message 消息 63 * @param data 数据 64 */ 65 public CommonResult(boolean success, String message, T data) { 66 this.success = success; 67 this.message = message; 68 this.data = data; 69 } 70 //省略get set 71}
二、定义一个错误码枚举
在有需要国际化的项目,当然选择通过i18n来配置更好,此处为了简单直接采用枚举定义。这里定义的错误仅供参考,不同公司每个应用在实际情况下可能都不大一样。
1/** 2 * 错误代码枚举类 3 * 4 */ 5public enum ErrorCodeEnum { 6 7 SUCCESS("0000", "success"), 8 9 PARAM_EMPTY("1001", "必选参数为空"), 10 11 PARAM_ERROR("1002", "参数格式错误"), 12 13 UNKNOWN_ERROR("9999", "系统繁忙,请稍后再试...."); 14 15 private String code; 16 17 private String desc; 18 19 ErrorCodeEnum(String code, String desc) { 20 this.code = code; 21 this.desc = desc; 22 } 23 24 public String getCode() { 25 return this.code; 26 } 27 28 29 public String getDesc() { 30 return desc; 31 } 32 33 @Override 34 public String toString() { 35 return "ErrorCodeEnum{" + 36 "code='" + code + '\'' + 37 ", desc='" + desc + '\'' + 38 '}'; 39 } 40}
三、静态封装CommonResult
静态封装CommonResult主要是方便在项目中快速根据逻辑写返回结果代码。
1/** 2 * 公共响应结果成功失败的静态方法调用 3 * 4 */ 5public class ResultUtil { 6 7 8 /** 9 * return success 10 * 11 * @param data 12 * @return 13 */ 14 public static <T> CommonResult<T> returnSuccess(T data) { 15 CommonResult<T> result = new CommonResult(); 16 result.setCode(ErrorCodeEnum.SUCCESS.getCode()); 17 result.setSuccess(true); 18 result.setData(data); 19 result.setMessage(ErrorCodeEnum.SUCCESS.getDesc()); 20 return result; 21 } 22 23 /** 24 * return error 25 * 26 * @param code error code 27 * @param msg error message 28 * @return 29 */ 30 public static CommonResult returnError(String code, String msg) { 31 CommonResult result = new CommonResult(); 32 result.setCode(code); 33 result.setData(""); 34 result.setMessage(msg); 35 return result; 36 37 } 38 39 /** 40 * use enum 41 * 42 * @param status 43 * @return 44 */ 45 public static CommonResult returnError(ErrorCodeEnum status) { 46 return returnError(status.getCode(), status.getDesc()); 47 } 48}
四、定义BaseController来处理验证错误自定义错误码返回
1/** 2 * BaseController 3 * 4 */ 5public abstract class BaseController { 6 7 private static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class); 8 9 /** 10 * validate params 11 * 12 * @param bindingResult 13 * @return 14 */ 15 protected CommonResult validParams(BindingResult bindingResult) { 16 if (bindingResult.hasErrors()) { 17 FieldError fieldError = bindingResult.getFieldError(); 18 return processBindingError(fieldError); 19 } 20 return ResultUtil.returnSuccess(""); 21 } 22 23 /** 24 * 根据spring binding 错误信息自定义返回错误码和错误信息 25 * 26 * @param fieldError 27 * @return 28 */ 29 private CommonResult processBindingError(FieldError fieldError) { 30 String code = fieldError.getCode(); 31 LOGGER.debug("validator error code: {}", code); 32 switch (code) { 33 case "NotEmpty": 34 return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage()); 35 case "NotBlank": 36 return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage()); 37 case "NotNull": 38 return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage()); 39 case "Pattern": 40 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 41 case "Min": 42 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 43 case "Max": 44 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 45 case "Length": 46 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 47 case "Range": 48 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 49 case "Email": 50 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 51 case "DecimalMin": 52 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 53 case "DecimalMax": 54 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 55 case "Size": 56 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 57 case "Digits": 58 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 59 case "Past": 60 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 61 case "Future": 62 return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage()); 63 default: 64 return ResultUtil.returnError(ErrorCodeEnum.UNKNOWN_ERROR); 65 } 66 } 67}
五、验证实例
这里直接给出一个简单的参数验证例子。
Controller继承上面写的BaseController
1/** 2 * 关于Validator使用测试 3 * 4 */ 5 6@RestController 7@RequestMapping("validator") 8public class ValidatorTestController extends BaseController { 9 10 private static final Logger LOGGER = LoggerFactory.getLogger(ValidatorTestController.class); 11 12 /** 13 * validate验证测试 14 * 15 * @param leader 16 * @param bindingResult 17 * @return 18 */ 19 @PostMapping("/test") 20 public CommonResult testSimpleValidate(@Valid @RequestBody Leader leader, BindingResult bindingResult) { 21 LOGGER.debug("ReqParams:{}", JSON.toJSONString(leader)); 22 CommonResult result = validParams(bindingResult); 23 if (!result.isSuccess()) { 24 return result; 25 } 26 return ResultUtil.returnSuccess(""); 27 } 28}
入参对象Leader代码
1public class Leader { 2 3 /** 4 * 姓名 5 */ 6 @NotEmpty 7 private String name; 8 9 /** 10 * 生日 11 */ 12 @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", message = "出生日期格式不正确") 13 private String birthday; 14 15 /** 16 * 年龄 17 */ 18 @Min(value = 0) 19 private Integer age; 20 21 //省略gettes and setters 22 23}
这时项目已经已经完全可以根据验证错误来返回自定义的错误码和提示了。
本例所涉及源代码:https://github.com/shalousun/api-doc-test
总结
在一些对外服务提供restful的应用中,根据不同的验证错误返回其实是避免不了的。当然实现的方式可以有种,而本文所采用的方式相对来说简单易懂。
版权申明:转载请注明出处,否则后果自负