hibernate-validator校验参数(统一异常处理)(下)

简介: hibernate-validator校验参数(统一异常处理)

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

1673357766634.jpg


四、分组校验


通常,某些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不能为空

1673357818118.jpg


五、自定义校验


有时官方提供的注解规则不能满足我们的需要,这时就要自定义注解来自定义校验规则,举一个典型的应用场景,只接收给定的字符,其它字符都校验不通过。比如:性别只接收 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.测试

1673357887657.jpg


六、统一异常处理


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";
    }
}


1673357246785.jpg

1673357259154.jpg

1673357269039.jpg

相关文章
|
Oracle Java 关系型数据库
hibernate-validator校验参数(统一异常处理)(上)
hibernate-validator校验参数(统一异常处理)
hibernate-validator校验参数(统一异常处理)(上)
|
Java 数据库连接
SpringBoot 2.0参数校验Hibernate Validator
SpringBoot 2.0参数校验Hibernate Validator
SpringBoot 2.0参数校验Hibernate Validator
Springboot使用hibernate-validator实现参数校验
Springboot使用hibernate-validator实现参数校验
229 0
Springboot使用hibernate-validator实现参数校验
|
Java 数据库连接 程序员
SpringBoot 2.0参数校验Hibernate Validator
Spring Boot (v2.0.5.RELEASE)Hibernate Validator springboot起步依赖自动添加了对hibernate validator的依赖 hibernate validator依赖 或者也可以自己手动添加依赖 org.
4272 0
|
SQL Java 数据库连接
Hibernate-ORM:07.Hibernate中的参数绑定
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥-------------     本篇博客会讲解Hibernate中的参数绑定,就是相当于sql语句中的where后面的条件   一,讲解概述:   1.
1041 0
|
SQL Java 数据库连接
HIbernate 查询拼接参数
public List findByEid(List trailids, String eid) { // TODO Auto-generated method stub String hql = " from TrailTestModel where 1=1 "; ArrayList cou...
1009 0
|
29天前
|
SQL 缓存 Java
框架分析(9)-Hibernate
框架分析(9)-Hibernate