springMVC引入Validation的具体步骤详解
本文简单介绍如何引入validation的步骤,如何通过自定义validation减少代码量,提高生产力。特别提及:非基本类型属性的valid,GET方法的处理,validation错误信息的统一resolve。 本文中validation的实际实现委托给Hibernate validation处理 基本配置 pom引入maven依赖 <!-- validation begin --> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.0.Final</version> </dependency> <!-- validation end --> 增加validation配置 在spring-mvc-servlet.xml中增加如下配置: <mvc:annotation-driven validator="validator"> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <property name="validationMessageSource" ref="messageSource"/> </bean> //messageSource 为i18n资源管理bean,见applicationContext.xml配置 自定义exceptionHandler 个性化处理validation错误信息,返回给调用方的信息更加友好, 在applicationContext.xml中增加如下配置: <!-- 加载i18n消息资源文件 --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>errormsg</value> <value>validation_error</value> </list> </property> </bean> <bean id="validationExceptionResolver" class="com.*.exception.ValidationExceptionResovler"/> 在项目类路径上增加:validation_error_zh_CN.properties资源文件: #the error msg for input validation #common field.can.not.be.null={field}不能为空 field.can.not.be.empty={field}不能为空或者空字符串 field.must.be.greater.than.min={field}不能小于{value} field.must.be.letter.than.max={field}不能大于{value} ValidationExceptionResovler实现: ValidationExceptionResovler.java @Slf4j public class ValidationExceptionResovler extends AbstractHandlerExceptionResolver { public ValidationExceptionResovler() { // 设置order,在DefaultHandlerExceptionResolver之前执行 this.setOrder(0); } /** * Handle the case where an argument annotated with {@code @Valid} such as * an {@link } or {@link } argument fails validation. * <p> * 自定义ValidationException 异常处理器 * 获取到具体的validation 错误信息,并组装CommonResponse,返回给调用方。 * * @param request current HTTP request * @param response current HTTP response * @param handler the executed handler * @return an empty ModelAndView indicating the exception was handled * @throws IOException potentially thrown from response.sendError() */ @ResponseBody protected ModelAndView handleMethodArgumentNotValidException(BindingResult bindingResult,HttpServletRequest request,HttpServletResponse response,Object handler) throws IOException { List<ObjectError> errors = bindingResult.getAllErrors(); StringBuffer errmsgBF = new StringBuffer(); for (ObjectError error : errors) { String massage = error.getDefaultMessage(); errmsgBF.append(massage); errmsgBF.append("||"); } String errmsgString = errmsgBF.toString(); errmsgString = errmsgString.length() > 2 ? errmsgString.substring(0,errmsgString.length() - 2) : errmsgString; log.error("Validation failed! {} ",errmsgString); Map<String,Object> map = new TreeMap<String,Object>(); map.put("success",false); map.put("errorCode","9999"); map.put("errorMsg",errmsgString); ModelAndView mav = new ModelAndView(); MappingJackson2JsonView view = new MappingJackson2JsonView(); view.setAttributesMap(map); mav.setView(view); return mav; } @Override protected ModelAndView doResolveException(HttpServletRequest request,Object handler,Exception ex) { BindingResult bindingResult = null; if (ex instanceof MethodArgumentNotValidException) { bindingResult = ((MethodArgumentNotValidException) ex).getBindingResult(); } else if(ex instanceof BindException) { bindingResult = ((BindException) ex).getBindingResult(); } else { //other exception,ignore } if(bindingResult != null) { try { return handleMethodArgumentNotValidException(bindingResult,request,response,handler); } catch (IOException e) { log.error("doResolveException: ",e); } } return null; } } 在controller中增加@Valid @RequestMapping("/buy") @ResponseBody public BaseResponse buy(@RequestBody @Valid BuyFlowerRequest request) throws Exception { //...... } 在request bean上为需要validation的属性增加validation注解 @Setter @Getter public class BuyFlowerRequest { @NotEmpty(message = "{name.can.not.be.null}") private String name; } 二级对象的validation 上面的写法,只能对BuyFlowerRequest在基本类型属性上做校验,但是没有办法对对象属性的属性进行validation,如果需要对二级对象的属性进行validation,则需要在二级对象及二级对象属性上同时添加@Valid 和 具体的validation注解. 如下写法: @Setter @Getter public class BuyFlowerRequest { @NotEmpty(field = "花名") private String name; @Min(field = "价格",value = 1) private int price; @NotNull private List<PayType> payTypeList; } @Setter @Getter public class PayType { @Valid @Min(value = 1) private int payType; @Valid @Min(value = 1) private int payAmount; } 进一步减少编码量 为了减少编码工作量,通过自定义Validation注解,尝试将validation作用的filed名称传递到 错误信息的资源文件中,从而避免为每个域编写不同的message模版. 下面以重写的@NotNull为例讲解: 1、定义Validation注解,注意相比原生注解增加了field(),用于传递被validated的filed名字 NotNull.java @Target( { ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER }) @Constraint(validatedBy = { NotNullValidator.class }) @Retention(RetentionPolicy.RUNTIME) public @interface NotNull { String field() default ""; String message() default "{field.can.not.be.null}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } 2、定义Validator,所有的Validator均实现ConstraintValidator接口: NotNullValidator.java public class NotNullValidator implements ConstraintValidator<NotNull,Object> { @Override public void initialize(NotNull annotation) { } @Override public boolean isValid(Object str,ConstraintValidatorContext constraintValidatorContext) { return str != null; } } 3、在filed上加入Validation注解,注意指定filed值,message如果没有个性化需求,可以不用指明,validation组件会自行填充default message。 BuyFlowerRequest.java @Setter @Getter public class BuyFlowerRequest { @NotEmpty(field = "花名") private String name; @Min(field = "价格",value = 1) private int price; } 注:@NotNull注解已经支持对list的特殊校验,对于List类型节点,如果list==null || list.size() == 0都会返回false,validation失败。目前已按照此思路自定义实现了@NotNull、@NotEmpty、@Min、@Max注解,在goods工程中可以找到. 支持GET请求 上面的示例都是POST请求,@RequestBody可以 resolve POST请求,但是不支持GET请求,阅读spring的文档和源码,发现@ModelAttribute可以将GET请求resolve成Bean,且支持Validation。具体可以翻阅spring源码:ModelAttributeMethodProcessor.resolveArgument()方法。 使用示例: @RequestMapping(value = "/buy",method = RequestMethod.GET) @ResponseBody public BaseResponse detail(@Valid @ModelAttribute DetailFlowerRequest request) throws Exception { DetailFlowerResponse response = new DetailFlowerResponse(); response.setName(request.getName()); return ResultFactory.success(response,BaseResponse.class); } TODO 1、根据业务场景扩展validation,如:日期格式、金额等 2、支持多个field关系校验的validation 附:spring validation实现关键代码 @RequestBody 实现类:RequestResponseBodyMethodProcessor.java public Object resolveArgument(MethodParameter parameter,ModelAndViewContainer mavContainer,NativeWebRequest webRequest,WebDataBinderFactory binderFactory) throws Exception { Object arg = this.readWithMessageConverters(webRequest,parameter,parameter.getGenericParameterType()); String name = Conventions.getVariableNameForParameter(parameter); WebDataBinder binder = binderFactory.createBinder(webRequest,arg,name); if (arg != null) { this.validateIfApplicable(binder,parameter); if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder,parameter)) { throw new MethodArgumentNotValidException(parameter,binder.getBindingResult()); } } mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name,binder.getBindingResult()); return arg; } @ModelAttibute 实现类:ModelAttributeMethodProcessor.java public final Object resolveArgument(MethodParameter parameter,WebDataBinderFactory binderFactory) throws Exception { String name = ModelFactory.getNameForParameter(parameter); Object attribute = mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : this.createAttribute(name,binderFactory,webRequest); if (!mavContainer.isBindingDisabled(name)) { ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class); if (ann != null && !ann.binding()) { mavContainer.setBindingDisabled(name); } } WebDataBinder binder = binderFactory.createBinder(webRequest,attribute,name); if (binder.getTarget() != null) { if (!mavContainer.isBindingDisabled(name)) { this.bindRequestParameters(binder,webRequest); } this.validateIfApplicable(binder,parameter)) { throw new BindException(binder.getBindingResult()); } } Map<String,Object> bindingResultModel = binder.getBindingResult().getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return binder.convertIfNecessary(binder.getTarget(),parameter.getParameterType(),parameter); } 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。 您可能感兴趣的文章:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |