This article briefly introduces how to introduce validation steps, how to reduce the amount of code through custom validation and improve productivity. Special mention: valid of non-primitive type attributes, processing of GET methods, unified resolution of validation error messages.
The actual implementation of validation in this article is entrusted to Hibernate validation for processing
Basic configuration
Pom introduces maven dependencies
<!-- 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 -->
Add validation configuration
Add the following configuration to spring-mvc-servlet.xml:
<mvc:annotation-driven validator="validator"><bean id="validator"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <property name="validationMessageSource" ref="messageSource"//messageSource is an i18n resource management bean, see applicationContext.xml configuration
Custom exceptionHandler
Personalize the validation error message, and the information returned to the caller is more friendly. The following configuration is added to the applicationContext.xml:
<!-- Load the i18n message resource file--><bean id="messageSource"> <property name="basenames"> <list> <value>errormsg</value> <value>validation_error</value> </list> </property></bean><bean id="validationExceptionResolver"/>
Added to the project classpath: validation_error_zh_CN.properties resource file:
#the error msg for input validation#commonfield.can.not.be.null={field} cannot be empty field.can.not.be.empty={field} cannot be empty or empty string field.must.be.greater.than.min={field} cannot be less than {value}field.must.be.letter.than.max={field} cannot be greater than {value} ValidationExceptionResovler implementation:
ValidationExceptionResovler.java
@Slf4jpublic class ValidationExceptionResovler extends AbstractHandlerExceptionResolver { public ValidationExceptionResovler() { // Set order and execute this.setOrder(0); } /** * Handle the case where an argument annotated with {@code @Valid} such as * an {@link } or {@link } argument fails validation. * <p> * Custom ValidationException Exception handler* Get the specific validation Error message and assemble CommonResponse and return to the caller. * * @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 mass = 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, HttpServletResponse response, 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; }} Add @Valid in controller
@RequestMapping("/buy")@ResponseBodypublic BaseResponse buy(@RequestBody @Valid BuyFlowerRequest request) throws Exception { //......} Add validation annotation to the attributes that require validation on the request bean
@Setter@Getterpublic class BuyFlowerRequest {@NotEmpty(message = "{name.can.not.be.null}") private String name;} Validation of secondary objects
The above writing method can only verify the basic type attributes of BuyFlowerRequest, but there is no way to validate the attributes of the object attributes. If you need to validate the attributes of the secondary object, you need to add @Valid and specific validation annotations to the secondary object and the secondary object attributes at the same time.
The following writing method:
@Setter@Getterpublic class BuyFlowerRequest { @NotEmpty(field = "bianname") private String name; @Min(field = "price", value = 1) private int price; @NotNull private List<PayType> payTypeList;} @Setter@Getterpublic class PayType { @Valid @Min(value = 1) private int payType; @Valid @Min(value = 1) private int payAmount;} Further reduce the number of encodings
In order to reduce the encoding workload, through custom Validation annotations, try to pass the filed name of validation to the resource file of the error message, thereby avoiding writing different message templates for each domain.
Here is an example of the rewritten @NotNull:
1. Define Validation annotation, note that field() is added compared to native annotation, which is used to pass the filed name that is validated.
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. Define Validator, all Validators implement the ConstraintValidator interface:
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. Add Validation annotation to filed, pay attention to specifying filed value. If the message does not have a personalized requirement, you don’t need to specify it. The validation component will fill in the default message by itself.
BuyFlowerRequest.java
@Setter@Getterpublic class BuyFlowerRequest { @NotEmpty(field = "bianname") private String name; @Min(field = "price", value = 1) private int price;} Note: The @NotNull annotation already supports special verification of list. For List type nodes, if list==null || list.size() == 0 will return false and validation fails. Currently, the @NotNull, @NotEmpty, @Min, @Max annotations have been customized according to this idea, and can be found in the goods project.
Supports GET requests
The above examples are all POST requests. @RequestBody can resolve POST requests, but they do not support GET requests. Read spring's documentation and source code and found that @ModelAttribute can resolve GET requests into beans and support Validation. For details, you can read the spring source code: ModelAttributeMethodProcessor.resolveArgument() method.
Example of usage:
@RequestMapping(value = "/buy", method = RequestMethod.GET)@ResponseBodypublic BaseResponse detail(@Valid @ModelAttribute DetailFlowerRequest request) throws Exception { DetailFlowerResponse response = new DetailFlowerResponse(); response.setName(request.getName()); return ResultFactory.success(response, BaseResponse.class);} TODO
1. Expand validation according to business scenarios, such as: date format, amount, etc.
2. Support validation of multiple field relationship verification
Attachment: Spring validation implementation key code
@RequestBody
Implementation class: 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 = bindFactory.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;} @ModelAttuite
Implementation class: ModelAttributeMethodProcessor.java
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { String name = ModelFactory.getNameForParameter(parameter); Object attribute = mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : this.createAttribute(name, parameter, bindFactory, webRequest); if (!mavContainer.isBindingDisabled(name)) { ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class); if (ann != null && !ann.binding()) { mavContainer.setBindingDisabled(name); } } WebDataBinder binder = bindFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { if (!mavContainer.isBindingDisabled(name)) { this.bindRequestParameters(binder, webRequest); } this.validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(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);}The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.