SpringBoot使用JSR303参数校验
开发web项目有时候我们需要对controller层传过来的参数进行一些基本的校验,比如非空,非null,整数值的范围,字符串的个数,日期,邮箱等等。最常见的就是我们直接写代码校验,这样以后比较繁琐,而且不够灵活。 不能总是写繁琐的代码来实现吧。
使用JSR303来做参数校验就方便并且整洁很多了。
pop引入依赖
1 2 3 4 5 |
<!--JSR303校验的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> |
Controller中使用
1 2 3 4 5 |
@RequestMapping("/login") //@Valid是JSR303校验 public Result<Boolean> login(@Valid LoginVo loginVo){ } |
校验实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package com.springboot.SecKill.vo; import com.springboot.SecKill.validator.IsMobile; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotNull; /** * @author WilsonSong * @date 2018/8/2/002 */ public class LoginVo { @NotNull @IsMobile private String mobile; @NotNull @Length(min=32) 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 + '\'' + '}'; } } |
自定义校验
JSR303中给我们定义了一些常用的校验注解,如本文最后常用常用注解中所示,但是要是还不能满足学习怎么去自己定义注解呢?
参照@NotNull这个校验注解的定义方法,@NotNull是这么定义的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package javax.validation.constraints; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(NotNull.List.class) @Documented @Constraint( validatedBy = {} ) public @interface NotNull { String message() default "{javax.validation.constraints.NotNull.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface List { NotNull[] value(); } } |
参照上面,我们实现自己的注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package com.springboot.SecKill.validator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; /** * @author WilsonSong * @date 2018/8/2/002 */ @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @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 {}; } |
实现具体校验器
上面只是实现了自己定义的校验器的接口,具体的实现类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.springboot.SecKill.validator; import com.springboot.SecKill.util.ValidatorUtil; import org.springframework.util.StringUtils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * JSR303具体的校验器 * @author WilsonSong * @date 2018/8/2/002 */ public class IsMobileValidator implements ConstraintValidator<IsMobile, String> { public boolean required = false; //初始化 @Override public void initialize(IsMobile constraintAnnotation) { required = constraintAnnotation.required(); } //校验 @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { if(required){ //值是必须的就判断是否合法 return ValidatorUtil.isMobile(s); //不为空就判断格式 }else { //若不必须就判断是否有值 if (StringUtils.isEmpty(s)){ return true; }else { return ValidatorUtil.isMobile(s); //不为空就判断格式 } } } } |
异常处理
参数校验不通过就会产生错误信息,显示一大串例如
1 |
{"timestamp":"2018-08-02T13:07:50.890+0000","status":400,"error":"Bad Request","errors":[{"codes":["IsMobile.loginVo.mobile","IsMobile.mobile","IsMobile.java.lang.String","IsMobile"],"arguments":[{"codes":["loginVo.mobile","mobile"],"arguments":null,"defaultMessage":"mobile","code":"mobile"},true],"defaultMessage":"手机号码格式错误","objectName":"loginVo","field":"mobile","rejectedValue":"22111111111","bindingFailure":false,"code":"IsMobile"}],"message":"Validation failed for object='loginVo'. Error count: 1","path":"/login/do_login"} |
为了方便查看,统一进行异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.springboot.SecKill.exception; import com.springboot.SecKill.result.CodeMsg; import com.springboot.SecKill.result.Result; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import org.springframework.validation.BindException; import java.util.List; /** * @author WilsonSong * @date 2018/8/2/002 */ @ControllerAdvice @ResponseBody public class GlobalExceptionHandler { @ExceptionHandler(value = Exception.class) //拦截所有的异常 public Result<String> exceptionHandler(HttpServletRequest httpServletRequest, Exception e){ // 参数校验异常 if(e instanceof BindException){ 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); } } } |
全局的异常处理
上面的知识参数检验时候的异常处理,但是在工程中很多的异常,用全局的异常处理更加方便维护。
首先定义一个全局的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.springboot.SecKill.exception; import com.springboot.SecKill.result.CodeMsg; /** * @author WilsonSong * @date 2018/8/2/002 */ public class GlobalException extends RuntimeException { private static final long serialVersionUID = 1L; private CodeMsg cm; public GlobalException (CodeMsg cm){ super(cm.toString()); this.cm = cm; } public static long getSerialVersionUID() { return serialVersionUID; } public CodeMsg getCm() { return cm; } public void setCm(CodeMsg cm) { this.cm = cm; } } |
然后在全局异常处理器中添加这个全局的异常,也就是在GlobalExceptionHandler类中添加
1 2 3 4 |
if(e instanceof GlobalException){ GlobalException ex = (GlobalException) e; return Result.error(ex.getCm()); } |
然后在产生异常的地方直接抛出全局异常就可以了
1 2 3 |
if (loginVo == null){ throw new GlobalException(CodeMsg.SERVER_ERROR); } |
CodeMsg.SERVER_ERROR是自己定义的异常信息。
常用注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@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) 被注释的元素必须符合指定的正则表达式。 @Email 被注释的元素必须是电子邮件地址 @Length 被注释的字符串的大小必须在指定的范围内 @NotEmpty 被注释的字符串必须非空 @Range 被注释的元素必须在合适的范围内 |
版权声明:本文为weixin_41945228原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。