(5)SprintBoot 2.X JSR303 参数校验 + 全局异常处理

1.JSR303参数校验

1.1 JSR303简介

  • JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator。
  • 此实现与 Hibernate ORM 没有任何关系。 JSR 303 用于对 Java Bean 中的字段的值进行验证。
    Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。
  • 注:可以使用注解的方式进行验证

1.2 为什么使用JSR303

  • JSR 303 用于对Java Bean 中的字段的值进行验证,使得验证逻辑从业务代码中脱离出来。是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回。

1.3 常用注解

Constraint详细信息
@Null被注释的元素必须为 null
@NotNull被注释的元素必须不为 null
@AssertTrue被注释的元素必须为 true
@AssertFalse被注释的元素必须为 false
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min)被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Pattern(value)被注释的元素必须符合指定的正则表达式

1.4 代码实现

1.pom.xml中引入依赖
    <!--用于定义参数校验器 + 全局异常处理器-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
2.自定义参数校验器@IsMobile
  • 除了使用自带的注解之外,JSR303还允许自定义校验注解
  • 步骤1: 实现校验器类IsMobileValidator类,需要继承ConstraintValidator校验器,需要重写方法initialize(),指定注解接口 IsMobile,然后查看值是否为必须的,如果是必须的,那么isValid会进行验证逻辑。
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {
    private boolean required = false;
    //初始化指定注解接口
    @Override
    public void initialize(IsMobile isMobile) {
        required = isMobile.required();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        if (required) {
            return ValidatorUtil.isMobile(value);
        } else {
            if (StringUtils.isEmpty(value)) {
                return true;
            } else {
                return ValidatorUtil.isMobile(value);
            }
        }
    }
}
  • 步骤2: 自定义注解@IsMobile,并引入相关配置。@Constraint(validatedBy = { IsMobileValidator.class })。指定自定义的一个校验器。
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint( validatedBy = {IsMobileValidator.class})//引进校验器
public @interface IsMobile {

    boolean required() default true;//默认不能为空
    //假如手机号码格式错误将抛出绑定异常
    String message() default "手机号码格式错误";//校验不通过输出信息

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
  • 步骤3: 步骤1中使用的自定义的手机验证逻辑工具类ValidatorUtil的实现
public class ValidatorUtil {
    private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}");
    public static boolean isMobile(String src){
        if(StringUtils.isEmpty(src)){
            return false;
        }
        Matcher m = mobile_pattern.matcher(src);
        return m.matches();
    }
}
  • 步骤4: 登陆需要用的LoginVo,需要使用自带的检验注解
public class LoginVo {
    @NotNull
    @IsMobile
    private String mobile;//@IsMobile 假如手机号码格式错误将抛出绑定异常
    @NotNull
    private String password;

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "LoginVo{" +
                "mobile='" + mobile + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
3.具体用法
  • 在需要验证的参数前面打上标签注解@Valid,那么此注解就会自动对该Bean 进行参数校验。具体校验规则在该Bean内部实现。
    @RequestMapping("/do_login")
    @ResponseBody
    public Result<String> doLogin(HttpServletResponse response , @Valid LoginVo loginVo) {
        log.info(loginVo.toString());
        String token = userService.login(response,loginVo);
        return Result.success(token);
    }

2.全局异常处理

2.1 为什么引入全局异常处理?

  • 当定义了JSR303校验器后,校验不通过都会产生一个BindException( org.springframework.validation.BindException)和一大串错误信息(其中就包括校验的处理信息)。若要对异常处理,我们可以定义一个全局异常处理的拦截器。
  • 优点1: 可以实现对项目中所有产生的异常进行拦截,在同一个类中实现统一处理。避免异常漏处理的情况。
  • 优点2: 当Service 出现业务逻辑错误的时候,这个时候我们可以直接抛出异常,让拦截器来捕捉,捕捉之后,就不需要冗余的代码来return 一个不符合业务逻辑的返回值来作为输出。
  • 优点3: 当参数校验不通过的时候,输出也是Result(CodeMsg),传给前端用于前端显示获取处理

2.2 代码实现全局异常处理

2.2.1自定义全局异常类GlobalException,该类需要继承RunTimeException
public class GlobalException extends RuntimeException {
    private static final long servialVersionUID = 1L;
    private CodeMsg codeMsg;
    public GlobalException(CodeMsg codeMsg) {
        super(codeMsg.toString());
        this.codeMsg = codeMsg;
    }

    public CodeMsg getCodeMsg() {
        return codeMsg;
    }
}
2.2.2 自定义全局异常拦截器
  • 主要用到的是:@ControllerAdvice + @ExceptionHandler (Spring 框架)

  • @ControllerAdvice是一个@Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。

  • @ResponseBody
    为了方便输出,使得这个GlobalExceptionHandler类里面的方法跟我们Controller类一样是输出的信息,返回值Result类型,可以携带信息。当参数校验不通过的时候,输出也是Result(CodeMsg),传给前端用于前端显示获取处理。

  • @ExceptionHandler(value = Exception.class)用于指定需要拦截的异常及其子类异常

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
    @ExceptionHandler(value = Exception.class)//拦截所有异常
    public Result<String> exceptionHandler(HttpServletRequest request, Exception e){
        e.printStackTrace();
        if(e instanceof GlobalException) {
            GlobalException ex = (GlobalException)e;
            return Result.error(ex.getCodeMsg());
        }else if(e instanceof BindException) {
        	/*注意:此处的BindException 是 Spring 框架抛出的Validation异常*/
            BindException ex = (BindException)e;
            List<ObjectError> errors = ex.getAllErrors();//绑定错误返回很多错误,是一个错误列表,只需要第一个错误
            ObjectError error = errors.get(0);
            String msg = error.getDefaultMessage();
            return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg));//给状态码填充参数
        }else {
            return Result.error(CodeMsg.SERVER_ERROR);
        }
    }
}
2.2.3 业务逻辑代码使用全局异常
  • 全局异常处理场景:先检查异常类型,若是我们业务异常,返回即可。业务中发现异常直接抛出我们自定义的异常即可。
    public String login(HttpServletResponse response,LoginVo loginVo){
        if (loginVo == null) {
            throw new GlobalException(CodeMsg.SERVER_ERROR);
        }
        String mobile = loginVo.getMobile();
        String formPass = loginVo.getPassword();
        //判断手机号是否存在
        MiaoshaUser user = getById(Long.parseLong(mobile));
        if (user == null) {
            throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
        }
        //验证密码
        String dbPass = user.getPassword();
        String saltDB = user.getSalt();
        String calcPass = MD5Util.formPassToDBPass(formPass, saltDB);
        if (!calcPass.equals(dbPass)) {
            throw new GlobalException(CodeMsg.PASSWORD_ERROR);
        }
        String token = UUIDUtil.uuid();
        addCookie(response, token, user);
        return token;
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,我是一个AI语言模型,无法提供源代码。但我可以给出一个大致的思路供您参考。 首先,在Spring Boot项目中,需要在Maven或Gradle配置文件中添加JSR303依赖,例如: ```xml <!-- JSR303校验依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> ``` 其次,在需要校验的实体类中,使用JSR303提供的注解进行字段校验,如下所示: ```java public class Student { @NotNull(message = "姓名不能为空") private String name; @Max(value = 150, message = "年龄不能超过150岁") @Min(value = 0, message = "年龄不能小于0岁") private int age; // 省略其它字段 // ... } ``` 以上代码中,@NotNull注解表示该字段不允许为空,@Max和@Min注解表示该字段的值必须在指定范围内。 接下来,在需要校验的Controller中,使用@Valid注解标注待校验的实体类对象,并将BindingResult对象作为方法参数,用于存储校验结果: ```java @RestController @RequestMapping("/student") public class StudentController { @PostMapping("/add") public String addStudent(@Valid @RequestBody Student student, BindingResult bindingResult) { if (bindingResult.hasErrors()) { // 校验失败,返回错误信息 return bindingResult.getFieldError().getDefaultMessage(); } else { // 校验成功,执行添加操作 // ... return "success"; } } } ``` 最后,实现校验信息国际化需要在Spring Boot项目的配置文件中添加相关配置,并在资源目录下创建不同语言的properties文件来存放各种校验错误信息的key-value对,如下所示: ```yaml # 配置国际化 spring.messages.basename=messages # 指定默认语言 spring.messages.fallback-to-system-locale=false spring.messages.encoding=UTF-8 ``` 其中,messages.properties文件中存放默认语言(如中文)下的校验错误信息,messages_en_US.properties文件中存放英文下的校验错误信息。例如: messages.properties ```properties NotNull.student.name=姓名不能为空 Max.student.age=年龄不能超过150岁 Min.student.age=年龄不能小于0岁 ``` messages_en_US.properties ```properties NotNull.student.name=Name cannot be null. Max.student.age=Age cannot be greater than 150. Min.student.age=Age cannot be less than 0. ``` 这样,在校验失败时,只需返回对应的key值即可,在前端页面通过i18n框架自动加载对应语言的国际化信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值