最近有很多读者给阿粉留言,说怎么好久没看到我的文章了,在这里给大家说说。由于公众号不再按时间线排序,你会发现有时候还能看到几天前的文章。这不是BUG,而是公众号的改动。至于具体的排序标准,阿芬不是太清楚,大概跟你打开某个公众号的频率有关系吧。所以如果你想第一时间收到阿芬的文章,可以点击Java极客技术头像,然后点击右上角的三个点进去,设置【星标】。1、参数有效性验证的重要性就不多说了。即使前端已经对参数做了基本的校验,后端还是需要校验,防止不合规的数据直接进入后端。严重的话,甚至会直接导致系统崩溃!本文结合本人在项目中的实际使用经验,以实用性为主,对数据合法性校验做一个总结。不会的朋友可以学习一下,同时可以马上在项目中实践。下面通过几个例子来演示如何判断一个参数是否合法,直接上手吧!2、断言验证对于参数合法性的验证,初始方法比较简单,自定义一个异常类。publicclassCommonExceptionextendsRuntimeException{/**错误码*/privateIntegercode;/**错误信息*/privateStringmsg;//...set/getpublicCommonException(Stringmsg){super(msg);this.msg=msg;}publicCommonException(Stringmsg,Throwablecause){super(msg,cause);this.msg=msg;}}当判断某个参数不合法时,直接抛出异常!@RestControllerpublicclassHelloController{@RequestMapping("/upload")publicvoidupload(MultipartFilefile){if(file==null){thrownewCommonException("请选择上传一个文件!");}//...}}然后写一个统一的异常拦截器来处理抛出异常的程序。这种方法更直观。如果需要判断当前参数是否为空,长度是否超过最大长度,代码就有点多了!于是,编程界的大佬想到了一种更优雅、更省代码的方式,创建一个断言工具类,专门用来判断参数是否合法。如果没有,将抛出异常!/***断言工具类*/publicabstractclassLocalAssert{publicstaticvoidisTrue(booleanexpression,Stringmessage)throwsCommonException{if(!expression){thrownewCommonException(message);}}publicstaticvoidisStringEmpty(Stringparam,Stringmessage)throwsCommonException{if(StringUtils.isEmpty(param)){thrownewCommonException(消息);}}publicstaticvoidisObjectEmpty(Objectobject,Stringmessage)throwsCommonException{if(object==null)thrownewCommonException(消息);}}publicstaticvoidisCollectionEmpty(Collectioncoll,Stringmessage)throwsCommonException{if(coll==null||(coll.size()==0)){thrownewCommonException(message);}}}当我们需要校验参数时使用这个类,可以直接通过这个类完成基本的操作,如下:@RestControllerpublicclassHelloController{@RequestMapping("/save")publicvoidsave(Stringname,Stringemail){LocalAssert.isStringEmpty(name,"用户名不能为空!");LocalAssert.isStringEmpty(电子邮件,“邮件盒子不能为空!");//....}}和上一步相比,当需要判断的参数比较多的时候,代码显然要简洁很多!和这个工具类一样,spring也提供了断言工具类Assert,在3.注解验证使用注解来验证数据的合法性可以说是java界一个非常伟大的创新,使用这种方式不仅让代码变得非常简洁,而且读起来看起来非常赏心悦目!3.1.导入依赖包我们来看看具体的实践方法,以SpringBoot项目为例,如果需要使用注解校验,直接导入spring-boot-starter-web即可依赖包,注解验证相关的依赖包会自动进入到项目中!web时创建实体类,也会用到lombok插件,所以需要引入lombok依赖包!org.projectlomboklombok1.18.4提供如果是普通的Java项目,引入下面的依赖包即可!org.hibernate.validatorhibernate-validator<版本>6.0.9.Finaljavax.eljavax.el-api3.0.0org.glassfish.webjavax.el2.2.63.2。注解验证请求对象接下来我们创建一个实体User来模拟用户注册时的请求实体对象!@Data@EqualsAndHashCode(callSuper=false)@Accessors(chain=true)publicclassUser{@NotBlank(message="用户名不能为空!")privateStringuserName;@Email(message="邮箱格式不正确")@NotBlank(message="邮箱不能为空!")privateStringemail;@NotBlank(message="密码不能为空!")@Size(min=8,max=16,message="请输入8到16个字符的密码")privateStringuserPwd;@NotBlank(message="确认密码不能为空!")privateStringconfirmPwd;}创建web层的register()注册接口方法,在request参数中添加@Valid,如下:@RestControllerpublicclassUserController{@RequestMapping("/register")publicbooleanregister(@RequestBody@ValidUseruser){if(!user.getUserPwd().equals(user.getConfirmPwd())){thrownewCommonException("确认密码与密码不一致,请确认!");}//业务处理...returntrue;}}最后自定义一个全局异常处理器,用于处理异常消息,如下:@Slf4j@ConfigurationpublicclassGlobalWebMvcConfigimplementsWebMvcConfigurer{/***统一异常处理*@paramresolvers*/@OverridepublicvoidconfigureHandlerExceptionResolvers(Listresolvers){resolvers.add(newHandlerExceptionResolver(){@OverridepublicModelAndViewresolveException(HttpServletRequestrequest,HttpServletResponseresponse,Objecto,Exceptione){log.error([[统一异常拦截]请求出现异常,内容如下:",e);ModelAndViewmv=newModelAndView(newMappingJackson2JsonView());Stringuri=request.getRequestURI();if(einstanceofCommonException){//CommonExecption是自定义异常类抛出的异常printWrite(((CommonException)e).getMsg(),((CommonException)e).getData(),uri,mv);}elseif(einstanceofMethodArgumentNotValidException){//MethodArgumentNotValidException是注解校验异常类//获取注解校验异常信息Stringerror=((MethodArgumentNotValidException)e).getBindingResult().getFieldError().getDefaultMessage();printWrite(error,null,uri,mv);}else{printWrite(e.getMessage(),null,uri,mv);}returnmv;}});}/***异常封装对应的结果*@paramobject*/privatevoidprintWrite(Stringmsg,Objectobject,Stringuri,ModelAndViewmv){ResResultresResult=newResResult(uri,object);if(msg!=null){resResult.setMsg(msg);}if(log.isDebugEnabled()){log.debug([response]异常输出结果:"+JSONObject.toJSONString(resResult,SerializerFeature.WriteMapNullValue));}MapresultMap=BeanToMapUtil.beanToMap(resResult);mv.addAllObjects(resultMap);}}下面我们启动项目,使用postman测试一下,看看效果如何?测试字段是否为空测试邮箱是否合法测试密码长度是否符合要求测试密码是否与确认密码相同3.3.注解验证请求参数上面我们介绍了请求对象的验证方法,直接在方法上验证请求参数是不是同样有效呢?为了眼见为实,我们在方法上模拟验证请求参数,看看结果新建一个查询接口查询,如下@RestControllerpublicclassUserController{@PostMapping("/query")publicbooleanquery(@RequestParam("userId")@Valid@NotBlank(message="UserIDcannotbeempty")StringuserId){returntrue;}}使用Postman请求尝试,默认userId参数为null,结果如下:很明显query()方法中的参数注解校验无效!当我们给UserController类加上@Validated注解的时候!@RestController@ValidatedpublicclassUserController{@PostMapping("/query")publicbooleanquery(@RequestParam("userId")@Valid@NotBlank(message="用户ID不能为空")StringuserId){returntrue;}}使用postman请求重试,结果如下!很明显注解通过了校验,同时抛出异常ConstraintViolationException!当@Validated参数应用到类上时,它告诉Spring去验证方法中的请求参数!所有在实际开发中,我们都可以使用@Validated和@Valid注解的组合来验证方法中的请求参数和请求对象!同时,@Validated和@Valid注解不仅仅是在controller层面进行验证,还可以对任何Spring组件进行验证,比如Service层方法入参Validation!@Service@ValidatedpublicclassUserService{publicvoidsaveUser(@ValidUseruser){//daoinsert}}3.4。自定义注解校验默认情况下,依赖包已经为我们提供了很多校验注解,如下!由JSRValidation注解提供!HibernateValidator提供的验证注解,但在某些情况下,比如性别,这个参数可能需要我们自己验证。同时我们也可以自定义一个注解来完成参数的校验。实现方法如下!新建一个Sex注解,其中SexValidator类指的是具体的参数验证类@Target({FIELD})@Retention(RUNTIME)@Constraint(validatedBy=SexValidator.class)@Documentedpublic@interfaceSex{Stringmessage()default"的性别值不在可选范围内”;Class>[]groups()default{};Class[]payload()default{};}SexValidator类,从ConstraintValidator接口实现publicclassSexValidatorimplementsConstraintValidator{@OverridepublicbooleanisValid(Stringvalue,ConstraintValidatorContextcontext){SetsexSet=newHashSet();sexSet.add("Male");sexSet。add("Female");returnsexSet.contains(value);}}最后在User实体类中添加一个性别参数,使用自定义注解进行验证!@Data@EqualsAndHashCode(callSuper=false)@Accessors(chain=true)publicclassUser{@NotBlank(message="用户名不能为空!")privateStringuserName;@Email(message="邮件格式不正确")@NotBlank(message="邮箱地址不能为空!")privateStringemail;@NotBlank(message="密码不能为空!")@Size(min=8,max=16,message="请输入密码,长度为8到16个字符。")privateStringuserPwd;@NotBlank(message="确认密码不能为空!")privateStringconfirmPwd;/***自定义注解验证*/@Sex(message="Gender输入错误!")privateStringsex;}使用postman请求试一试,结果如下!如果不传sex参数,可以清楚的看到已经生效了!3.5.手动注解验证有时候,如果有100个类,我们需要使用验证注解。这时候我们可能会在每个类中添加注解@Validated或者@Valid,添加100个这样的类会造成大量的重复工作这个时候我们的诉求就是对带校验注解的实体类进行全局参数校验!解决方案将是使用Validator提供的手动注解验证工具类。实现方法如下!新建注解校验工具类/***注解校验工具类*/publicclassValidatorUtils{/***获取对象中所有注解校验异常信息*@paramobject*@return*/publicstaticStringvalidated(Objectobject){ListerrorMessageList=newArrayList<>();//获取注解验证工厂ValidatorFactoryfactory=Validation.buildDefaultValidatorFactory();Validatorvalidator=factory.getValidator();Set>violations=validator.validate(object);for(ConstraintViolation