controller
@RestController() @RequestMapping("/nest") public class NestValidateController { @PostMapping("/validate") public ResponseEntity<String> validateNestingAttr(@Valid @RequestBody Input input) { return ResponseEntity.ok("valid"); } }
注意:此时用@Valid和@Validated都可以,总结一下:在使用@RequestBody接收json数据时,必须在方法的参数列表里面使用@Valid或@Validated来告诉hibernate-validator需要校验参数封装实体的字段约束(被标注到字段上的校验注解),然后在需要嵌套校验的字段上标注@Valid注解,因为@Validated是不能使用在类的字段上的,所以只能使用@Valid
四、分组校验
通常,某些Java Bean在不同的请求之间共享。以典型的CRUD操作为例:Create请求和Update请求很可能都采用相同的对象类型作为输入。但是,在不同的情况下可能会触发不同的验证。
正确使用的示例:
@Data public class User { @NotNull(groups = OnUpdate.class) @Null(groups = OnCreate.class) private Long id; @NotEmpty(groups = OnCreate.class) private String userName; @NotEmpty(groups = OnCreate.class) private String mobile; // 仅仅作为一个标记接口 public interface OnUpdate{} public interface OnCreate{} }
三个字段标明在创建操作时需要校验
controller
@RestController @RequestMapping("/group") public class GroupValidateController { @PostMapping("/user") public ResponseEntity<String> save(@Validated(value = {User.OnCreate.class}) @RequestBody User user) { return ResponseEntity.ok("valid"); } }
方法参数中标明需要校验的分组
只有@Validated才支持分组校验,所以这里必须使用@Validated,不能用@Valid替换
此时生效的是:@Null(groups = OnCreate.class),id必须为空,userName和mobile不能为空
五、自定义校验
有时官方提供的注解规则不能满足我们的需要,这时就要自定义注解来自定义校验规则,举一个典型的应用场景,只接收给定的字符,其它字符都校验不通过。比如:性别只接收 M,F;
1.定义一个注解
/** * @Constraint: 关联解析类 * @Target: 注解作用于的位置 */ @Constraint(validatedBy = EnumValueValidator.class) @Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface EnumValue { String[] value() default ""; String message() default "参数必须为指定的值"; Class<?>[] groups() default {}; Class<? extends javax.validation.Payload>[] payload() default {}; }
2.定义校验的规则
public class EnumValueValidator implements ConstraintValidator<EnumValue,String> { private String[] enumValue; /** * 初始化时把注解中的值传过来 * @param constraintAnnotation */ @Override public void initialize(EnumValue constraintAnnotation) { this.enumValue = constraintAnnotation.value(); } /** * * @param source 是要被校验的值 * @param context * @return */ @Override public boolean isValid(String source, ConstraintValidatorContext context) { if(source instanceof String) { for (String val : enumValue) { if(val.equals(source)) { return true; } } }else { throw new IllegalArgumentException("参数类型非法"); } return false; } }
3.controller中使用
@RestController @RequestMapping("/def") @Validated // 这个注解一定不能忘 public class DefinationValidator { @GetMapping("/save") public ResponseEntity<String> save( @EnumValue(value = {"F","M"},message = "性别只能传F,M") @RequestParam("gender") String gender, @EnumValue(value = {"1","2","3","4","5","6","7"},message = "星期只能传1-7") @RequestParam("week") String week ) { return ResponseEntity.ok("valid"); } }
4.测试
六、统一异常处理
1.定义一个异常信息描述基础信息接口类
public interface ResultCodeInterface { /*错误描述*/ String getMsg(); /*错误码*/ int getCode(); }
2.定义一个枚举类实现上面的异常信息描述接口
public enum CommonEnum implements ResultCodeInterface { // 数据操作错误定义 SUCCESS(200, "成功!"), UNSUCCESS(400, "失败"), SIGNATURE_NOT_MATCH(401, "请求的数字签名不匹配!"), BODY_NOT_MATCH(402, "请求的数据格式不符!"), REQUEST_NOT_MATCH(403,"请求不合法"), NOT_FOUND(404, "未找到该资源!"), INTERNAL_SERVER_ERROR(500, "服务器内部错误!"), SERVER_BUSY(503, "服务器正忙,请稍后再试!"); /** * 错误码 */ private int code; /** * 错误描述 */ private String msg; CommonEnum(int code, String resultMsg) { this.code = code; this.msg = resultMsg; } @Override public String getMsg() { return msg; } @Override public int getCode() { return code; } }
3.定义一个自定义异常类,标识业务系统出现的异常信息
public class BaseException extends RuntimeException { private int code; private String msg; private Object data; public BaseException(String msg) { super(msg); } public BaseException(Exception e) { this(e.getMessage()); } public BaseException(String message, Throwable cause) { super(message, cause); } public BaseException(int code, String msg, Throwable cause) { super(msg, cause); this.code = code; this.msg = msg; } public BaseException(int code, String msg) { this.code = code; this.msg = msg; } public BaseException(int code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; } public BaseException(ResultCodeInterface e) { this.code = e.getCode(); this.msg = e.getMsg(); } public int getCode() { return code; } public String getMsg() { return msg; } public Object getData() { return data; } public void setCode(int code) { this.code = code; } public void setMsg(String msg) { this.msg = msg; } public void setData(Object data) { this.data = data; } @Override public String toString() { return "BaseException{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + ", data=" + data + '}'; } }
public class RemoteException extends BaseException{ public RemoteException(ResultCodeInterface codeInterface, String message) { super(codeInterface.getCode(), codeInterface.getMsg() + ":" + message); } public RemoteException(String msg) { super(msg); } public RemoteException(Exception e) { super(e); } public RemoteException(String message, Throwable cause) { super(message, cause); } public RemoteException(int code, String msg, Throwable cause) { super(code, msg, cause); } public RemoteException(ResultCodeInterface codeInterface) { super(codeInterface.getCode(), codeInterface.getMsg()); } public RemoteException(int code, String msg, Object data) { super(code, msg, data); } }
4.定义一个统一结果返回数据封装类
@JsonInclude(value= JsonInclude.Include.NON_NULL) public class Result<T> implements Serializable { private int code; private String msg; private T data; public Result() { } public void setCode(int code) { this.code = code; } public void setMsg(String msg) { this.msg = msg; } public void setData(T data) { this.data = data; } public int getCode() { return code; } public String getMsg() { return msg; } public T getData() { return data; } public Result(int code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } public Result(int code, String msg) { this.code = code; this.msg = msg; } public static Result success(Object data) { return new Result(CommonEnum.SUCCESS.getCode(), CommonEnum.SUCCESS.getMsg(), data); } public static Result error(String msg) { return new Result(CommonEnum.UNSUCCESS.getCode(), CommonEnum.UNSUCCESS.getMsg(), msg); } public static Result error(int code, String msg) { return new Result(code, msg); } public static Result error(ResultCodeInterface errorInfo) { Result rs = new Result(); rs.setCode(errorInfo.getCode()); rs.setMsg(errorInfo.getMsg()); return rs; } }
5.定义一个全局异常处理类
定义全局异常处理类后,会对程序运行过程中出现的异常进行统一处理。
@RestControllerAdvice public class GlobalExceptionHandler { /** * 参数不合法异常 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Result validExceptionHandler(MethodArgumentNotValidException e) { String msg = e.getBindingResult().getAllErrors().stream().map(item -> item.getDefaultMessage()).collect(Collectors.joining()); return Result.error(msg); } /** * POST缺少body参数 **/ @ExceptionHandler(HttpMessageNotReadableException.class) public Result httpMessageNotReadableHandler(HttpMessageNotReadableException e) { return Result.error(CommonEnum.BODY_NOT_MATCH); } /** * 处理url参数异常 **/ @ExceptionHandler(ConstraintViolationException.class) public Result constrainViolationHandler(ConstraintViolationException e) { String msg = e.getConstraintViolations().stream().map(item -> item.getMessage()).collect(Collectors.joining()); return Result.error(msg); } /** * 未传入参数异常 @NotBlank **/ @ExceptionHandler(MissingServletRequestParameterException.class) public Result missingServletRequestParameterHandler(MissingServletRequestParameterException e) { return Result.error(CommonEnum.BODY_NOT_MATCH); } /** * 请求方式不正确 **/ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public Result httpRequestMethodNotSupportedHandler(HttpRequestMethodNotSupportedException e) { return Result.error(CommonEnum.REQUEST_NOT_MATCH); } /** * 缺少请求参数 **/ @ExceptionHandler(BindException.class) public Result bindExceptionHandler(BindException e) { return Result.error(CommonEnum.REQUEST_NOT_MATCH); } /** * 业务异常 **/ @ExceptionHandler(RemoteException.class) public Result remoteExceptionHandler(RemoteException e) { return Result.error(e.getCode(),e.getMsg()); } @ExceptionHandler(Exception.class) public Result exceptionHandler(Exception e) { return Result.error(CommonEnum.INTERNAL_SERVER_ERROR); } }
6.统一异常处理测试
定义业务枚举异常
public enum UserCodeEnum implements ResultCodeInterface { // USER_ADD_EXISTS(1001, "账号已存在,请重新输入"), VERIFY_EXISTS(1002, "刷新过于频繁,请稍后再试"), SIGN_CACHE_NOT_FOUND(1002, "验证码不存在"), SIGN_CACHE_NO_EQUAL(1003, "验证码错误"); /** * 处理结果码 */ public int code; /** * 结果描述 */ public String msg; UserCodeEnum(int code, String msg) { this.code = code; this.msg = msg; } @Override public String getMsg() { return msg; } @Override public int getCode() { return code; } }
@RestController @RequestMapping("/hello") @Validated public class HelloController { @GetMapping("/hello") public String hello( @NotBlank(message = "id is null") @RequestParam("id") String id) { if(true){ throw new RemoteException(UserCodeEnum.SIGN_CACHE_NO_EQUAL); } return "hello"; } }