简单实现spring web工程下的请求参数校验

spring下的数据校验

      • 前言
      • 概念
      • 检验类型
      • 检验实现
        • 创建工程
        • 引入swagger
        • 新建`UserController`类
        • 运行系统
        • 测试
      • spring校验功能源码解析
      • 分组检验
        • 新建注解类
        • 在`UserController`新增修改和新增接口
        • 运行&测试
      • 自定义检验
      • 手动检验

前言

数据的校验是web后端(服务端)一个不可或缺的功能,前端的js校验可以涵盖大部分的校验规则,如生日格式,邮箱格式校验等。但是为了避免用户绕过前端的js(浏览器),使用http工具直接向后端请求一些违法数据,web后端的数据校验也是必要的,可以防止脏数据落到数据库中。

概念

Bean Validation是Java定义的一套基于注解的数据校验规范,目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已经经历了三个版本。规范注解如:@Null@NotNull@Patternjavax.validation.constraints包下,只提供规范不提供实现。

Hibernate Validator则是Bean Validation的参考实现,它提供了Bean Validation规范中所有内置constraint的实现,除此之外还有一些附加的constraint,如@Email,@Length,@Range等,位于org.hibernate.validator.constraints包下。

Spring validationhibernate-validation进行了二次封装,显示校验validated bean时,你可以使用Spring validation或者hibernate validation,而spring validation另一个特性,便是在springmvc模块中添加了自动检验,并将校验信息封装进了特定的类中,位于org.springframework.validation包下。

检验类型

注解 说明
@Null 验证对象是否为空
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格
@NotEmpty 检查约束元素是否为NULL或者是EMPTY
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) 验证字符串长度是否在给定的范围之内
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度
@Range(min=, max=) 验证值是不是在该范围内
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
@CreditCardNumber 信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证

检验实现

创建工程

  1. 选中idea菜单File,从弹出的子菜单中选择New,再从子菜单中选择并点击Project...,如下图
    简单实现spring web工程下的请求参数校验_第1张图片
  2. 经过第一步的操作,会弹出如下图的界面,选择Spring Initializer,默认Project SDKInitializr Service URL的值,点击next按钮。
    简单实现spring web工程下的请求参数校验_第2张图片
  3. 经过第二步的操作,会弹出如下界面,设置GroupArtifact,点击Next按钮
    简单实现spring web工程下的请求参数校验_第3张图片
  4. 经过第三步,会弹出如下界面,选择Developer Tools,选中Lombok,选择Web,选中Srping Web Starter,点击Next按钮 简单实现spring web工程下的请求参数校验_第4张图片
  5. 经过第四步操作,会弹出如下界面,设置Project NameProject location,点击finish按钮 ,到此我们的项目创建完毕。接下来开始写代码简单实现spring web工程下的请求参数校验_第5张图片

引入swagger

  1. 在pom.xml文件中加入如下依赖
		
			io.springfox
			springfox-swagger2
			2.6.1
		
		
			io.springfox
			springfox-swagger-ui
			2.6.1
		
		
			io.springfox
			springfox-bean-validators
			2.6.1
		

		
			io.springfox
			springfox-bean-validators
			2.6.1
		
  1. com.study.web包下新建config包,在com.study.web.config包下,新建SwaggerConfig类,代码如下:
package com.study.web.config;

import static springfox.documentation.builders.PathSelectors.ant;
import com.google.common.base.Predicates;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * swagger工具配置
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurerAdapter {

    @Value("${swaggerUrl}")
    private String swaggerUrl;

    /**
     * SpringBoot默认已经将classpath:/META-INF/resources/和classpath:/META-INF/resources/webjars/映射
     * 所以该方法不需要重写,如果在SpringMVC中,可能需要重写定义(我没有尝试) 重写该方法需要 extends WebMvcConfigurerAdapter
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Bean
    public Docket restApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .host(swaggerUrl)
                //.apiInfo(apiInfo())
                .select()
                .paths(Predicates.and(ant("/**"), Predicates.not(ant("/error"))))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Swagger Petstore")
                .description("Petstore API Description")
                .contact(new Contact("leongfeng", "http:/test-url.com", "[email protected]"))
                .license("Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .version("1.0.0")
                .build();
    }
}

新建UserController

  1. 在com.study.web.controller包下,新增UserController类,代码如下:
package com.study.web.controller;

import com.study.web.domain.vo.SaveUserVo;
import com.study.web.domain.vo.UpdateUserVo;
import com.study.web.domain.vo.UserVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author ZOUZHIHUI
 * @Date 2019-06-22
 */
@RestController("user")
@Api(value = "UserController", description = "用户接口")
public class UserController {

    @RequestMapping(value = "save", method = RequestMethod.POST)
    @ApiOperation(value = "save", nickname = "保存用户信息")
    public String save(@Validated @RequestBody SaveUserVo userVo) {
        return "success";
    }
}
  1. SaveUserVo类如下:
package com.study.web.domain.vo;

import javax.validation.constraints.NotEmpty;
import lombok.Data;

/**
 * @author ZOUZHIHUI
 * @Date 2019-06-22
 */
@Data
public class SaveUserVo {

    @NotEmpty(message = "用户名不能为空!")
    private String username;

    @NotEmpty(message = "密码不能为空!")
    private String password;

}

运行系统

  1. 配置application.properties文件
server.port=8989
server.servlet.context-path=/study-web

swaggerUrl: localhost:8989
  1. 找到StudyWebApplication类,右键debug运行,服务启动完之后,在浏览器上输入如下地址:http://localhost:8989/study-web/swagger-ui.html,能正常访问表示服务没问题。

测试

  1. 访问该地址http://localhost:8989/study-web/swagger-ui.html,找到user-controllersave接口,按下图,配置参数,点击Try it out按钮
    简单实现spring web工程下的请求参数校验_第6张图片
  2. 会得到如下的响应结果,表明验证功能生效
{
  "timestamp": "2019-06-26T02:45:25.879+0000",
  "status": 400,
  "error": "Bad Request",
  "errors": [
    {
      "codes": [
        "NotEmpty.saveUserVo.username",
        "NotEmpty.username",
        "NotEmpty.java.lang.String",
        "NotEmpty"
      ],
      "arguments": [
        {
          "codes": [
            "saveUserVo.username",
            "username"
          ],
          "arguments": null,
          "defaultMessage": "username",
          "code": "username"
        }
      ],
      "defaultMessage": "用户名不能为空!",
      "objectName": "saveUserVo",
      "field": "username",
      "rejectedValue": "",
      "bindingFailure": false,
      "code": "NotEmpty"
    },
    {
      "codes": [
        "NotEmpty.saveUserVo.password",
        "NotEmpty.password",
        "NotEmpty.java.lang.String",
        "NotEmpty"
      ],
      "arguments": [
        {
          "codes": [
            "saveUserVo.password",
            "password"
          ],
          "arguments": null,
          "defaultMessage": "password",
          "code": "password"
        }
      ],
      "defaultMessage": "密码不能为空!",
      "objectName": "saveUserVo",
      "field": "password",
      "rejectedValue": "",
      "bindingFailure": false,
      "code": "NotEmpty"
    }
  ],
  "message": "Validation failed for object='saveUserVo'. Error count: 2",
  "path": "/study-web/save"
}

spring校验功能源码解析

  1. 参数验证调用流程,如下图,主要介绍几个重要流程
    简单实现spring web工程下的请求参数校验_第7张图片
  2. HandlerMethodArgumentResolverComposite类,该类实现了HandlerMethodArgumentResolver接口,该类存放了spring提供的所有参数解析器类。会根据请求参数来找到唯一的参数解析器类,本例子中是RequestResponseBodyMethodProcessor。也就是说不同从参数会有不同的解析器类。列表如下:
类名 描述
RequestParamMethodArgumentResolver 针对被 @RequestParam 注解修饰, 但类型不是 Map, 或类型是 Map, 并且 @RequestParam 中指定 name, 一般通过 MultipartHttpServletRequest
RequestParamMapMethodArgumentResolver 针对被 @RequestParam注解修饰, 且参数类型是 Map 的, 且 @RequestParam 中没有指定 name, 从 HttpServletRequest 里面获取所有请求参数, 最后封装成 LinkedHashMap
PathVariableMethodArgumentResolver 解决被注解 @PathVariable 注释的参数 <- 这个注解对应的是 uri 中的数据, 在解析 URI 中已经进行解析好了 <- 在 RequestMappingInfoHandlerMapping.handleMatch -> getPathMatcher().extractUriTemplateVariables
PathVariableMapMethodArgumentResolver 针对被 @PathVariable 注解修饰, 并且类型是 Map的, 且 @PathVariable.value == null, 从 HttpServletRequest 中所有的 URI 模版变量 (PS: URI 模版变量的获取是通过 RequestMappingInfoHandlerMapping.handleMatch 获取)
MatrixVariableMethodArgumentResolver 针对被 @MatrixVariable 注解修饰的参数起作用, 从 HttpServletRequest 中获取去除 ; 的 URI Template Variables 获取数据
MatrixVariableMapMethodArgumentResolver 针对被 @MatrixVariable 注解修饰, 并且类型是 Map的, 且 MatrixVariable.name == null, 从 HttpServletRequest 中获取 URI 模版变量 <-- 并且是去除 ;
ServletModelAttributeMethodProcessor 针对被@ModeAttribute注解修饰的
RequestResponseBodyMethodProcessor 解决被 @RequestBody 注释的方法参数 <- 其间是用 HttpMessageConverter 进行参数的转换
RequestPartMethodArgumentResolver 参数被 @RequestPart 修饰, 参数是 MultipartFile
RequestHeaderMethodArgumentResolver 针对 参数被 RequestHeader 注解, 并且 参数不是 Map 类型, 数据通过 HttpServletRequest.getHeaderValues(name) 获取
RequestHeaderMapMethodArgumentResolver 解决被 @RequestHeader 注解修饰, 并且类型是 Map 的参数, HandlerMethodArgumentResolver会将 Http header 中的所有 name <–> value 都放入其中
ServletCookieValueMethodArgumentResolver 针对被 @CookieValue 修饰, 通过 HttpServletRequest.getCookies 获取对应数据
ExpressionValueMethodArgumentResolver 针对被 @Value 修饰, 返回 ExpressionValueNamedValueInfo
SessionAttributeMethodArgumentResolver 针对 被 @SessionAttribute 修饰的参数起作用, 参数的获取一般通过 HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_SESSION)
RequestAttributeMethodArgumentResolver 针对 被 @RequestAttribute 修饰的参数起作用, 参数的获取一般通过 HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_REQUEST)
ServletRequestMethodArgumentResolver 支持 WebRequest, ServletRequest, MultipartRequest, HttpSession, Principal, InputStream, Reader, HttpMethod, Locale, TimeZone, 数据通过 HttpServletRequest 获取
ServletResponseMethodArgumentResolver 支持 ServletResponse, OutputStream, Writer 类型, 数据的获取通过 HttpServletResponse
HttpEntiyMethodProcessor 针对 HttpEntity,RequestEntity 类型的参数进行参数解决, 将 HttpServletRequest 里面的数据转换成 HttpEntity
RedirectAttributesMethodArgumentResolver 针对 RedirectAttributes及其子类的参数 的参数解决器, 主要还是基于 NativeWebRequest && DataBinder (通过 dataBinder 构建 RedirectAttributesModelMap)
ModelMethodProcessor 针对 Model 及其子类的参数, 数据的获取一般通过 ModelAndViewContainer.getModel()
MapMethodProcessor 针对参数是 Map, 数据直接从 ModelAndViewContainer 获取 Model
ErrorsMethodArgumentResolver 参数是Errors
SessionStatusMethodArgumentResolver 支持参数类型是 SessionStatus, 直接通过 ModelAndViewContainer 获取 SessionStatus
UriComponentsBuilderMethodArgumentResolver 支持参数类型是 UriComponentsBuilder, 直接通过 ServletUriComponentsBuilder.fromServletMapping(request) 构建对象

参考:
spring mvc 数据绑定
SpringMVC 4.3 源码分析之 HandlerMethodArgumentResolver

  1. RequestResponseBodyMethodProcessor来解析具体参数,
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		parameter = parameter.nestedIfOptional();
		// 从request中读取参数,并转成对应的参数对象
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		String name = Conventions.getVariableNameForParameter(parameter);

		if (binderFactory != null) {
			// 创建WebDataBinder,此处默认创建ExtendedServletRequestDataBinder
			WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
			if (arg != null) {
				// 执行参数验证,如果验证失败,会把错误信息封装到binder中
				validateIfApplicable(binder, parameter);
				// 判断binder中是否有验证失败的信息,如果有,则抛出异常
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
				}
			}
			if (mavContainer != null) {
				mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
			}
		}
		
		return adaptArgumentIfNecessary(arg, parameter);
	}
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
		// 获取参数的注解
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation ann : annotations) {
		    // 获取Validated注解
			Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
			// 判断是否有Validated注解或者是以Valid开头的注解,匹配@Valid注解
			if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
			   // 获取注解中的值,主要是用于group分组检验
				Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
				Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
				// 调用ExtendedServletRequestDataBinder类的validate()方法
				binder.validate(validationHints);
				break;
			}
		}
	}
  1. ExtendedServletRequestDataBinder,该方法遍历所有可用的验证器,并调用每个验证器的validate方法
public void validate(Object... validationHints) {
		Object target = getTarget();
		Assert.state(target != null, "No target to validate");
		BindingResult bindingResult = getBindingResult();
		// Call each validator with the same binding result
		for (Validator validator : getValidators()) {
			// 判断是否有分组验证功能,如果有则走下面的验证
			if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
				((SmartValidator) validator).validate(target, bindingResult, validationHints);
			}
			else if (validator != null) { // 没有分组验证,则走下面的验证
				validator.validate(target, bindingResult);
			}
		}
	}
  1. ValidatorAdapter
public void validate(Object target, Errors errors) {
		this.target.validate(target, errors);
	}
  1. SpringValidatorAdapter类中会调用hibernate-validate中的ValidatorImpl来实现具体的验证,并解析返回验证信息,放入Errors中,关于ValidatorImpl的具体验证规则,后续再分析
public void validate(Object target, Errors errors) {
		if (this.targetValidator != null) {
			processConstraintViolations(this.targetValidator.validate(target), errors);
		}
	}
protected void processConstraintViolations(Set> violations, Errors errors) {
		for (ConstraintViolation violation : violations) {
			String field = determineField(violation);
			FieldError fieldError = errors.getFieldError(field);
			if (fieldError == null || !fieldError.isBindingFailure()) {
				try {
					ConstraintDescriptor cd = violation.getConstraintDescriptor();
					String errorCode = determineErrorCode(cd);
					Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, cd);
					if (errors instanceof BindingResult) {
						// Can do custom FieldError registration with invalid value from ConstraintViolation,
						// as necessary for Hibernate Validator compatibility (non-indexed set path in field)
						BindingResult bindingResult = (BindingResult) errors;
						String nestedField = bindingResult.getNestedPath() + field;
						if (nestedField.isEmpty()) {
							String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
							ObjectError error = new ObjectError(
									errors.getObjectName(), errorCodes, errorArgs, violation.getMessage()) {
								@Override
								public boolean shouldRenderDefaultMessage() {
									return requiresMessageFormat(violation);
								}
							};
							error.wrap(violation);
							bindingResult.addError(error);
						}
						else {
							Object rejectedValue = getRejectedValue(field, violation, bindingResult);
							String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
							FieldError error = new FieldError(errors.getObjectName(), nestedField,
									rejectedValue, false, errorCodes, errorArgs, violation.getMessage()) {
								@Override
								public boolean shouldRenderDefaultMessage() {
									return requiresMessageFormat(violation);
								}
							};
							error.wrap(violation);
							bindingResult.addError(error);
						}
					}
					else {
						// got no BindingResult - can only do standard rejectValue call
						// with automatic extraction of the current field value
						errors.rejectValue(field, errorCode, errorArgs, violation.getMessage());
					}
				}
				catch (NotReadablePropertyException ex) {
					throw new IllegalStateException("JSR-303 validated property '" + field +
							"' does not have a corresponding accessor for Spring data binding - " +
							"check your DataBinder's configuration (bean property versus direct field access)", ex);
				}
			}
		}
	}
 
  

分组检验

新建注解类

  1. com.study.web.group包下,新增SaveUpdate两个接口类,主要对应新增和修改两个接口
package com.study.web.group;

/**
 * @author ZOUZHIHUI
 * @Date 2019-06-27
 */
public interface Save {

}
package com.study.web.group;

/**
 * @author ZOUZHIHUI
 * @Date 2019-06-27
 */
public interface Update {

}

UserController新增修改和新增接口

  1. 在新增方法中使用@Validate注解,value{Save.class}。在修改方法中使用@Validate注解,value{Update.class}
@RequestMapping(value = "save2", method = RequestMethod.POST)
    @ApiOperation(value = "save2", nickname = "保存用户信息2")
    public String save2(@Validated({Save.class}) @RequestBody UserVo userVo) {
        return "success";
    }

    @RequestMapping(value = "update2", method = RequestMethod.POST)
    @ApiOperation(value = "update2", nickname = "更新用户信息2")
    public String update2(@Validated({Update.class}) @RequestBody UserVo userVo) {
        return "success";
    }
  1. UserVo代码如下, 当新增时,usernamepassword参数不能为空;当修改时,idusername不能为空。
package com.study.web.domain.vo;

import com.study.web.group.Save;
import com.study.web.group.Update;
import javax.validation.constraints.NotEmpty;
import lombok.Data;

/**
 * @author ZOUZHIHUI
 * @Date 2019-06-22
 */
@Data
public class UserVo {

    @NotNull(groups = {Update.class})
    private Integer id;

    @NotEmpty(groups = {Save.class, Update.class})
    private String username;

    @NotEmpty(groups = {Save.class})
    private String password;

}

运行&测试

  1. 访问该地址http://localhost:8989/study-web/swagger-ui.html,找到user-controllersave2接口,按下图,配置参数,点击Try it out按钮简单实现spring web工程下的请求参数校验_第8张图片
  2. 会得到如下的响应结果,说明分组验证功能成功,新增是usernamepassword不能为空
{
  "timestamp": "2019-06-27T02:32:22.815+0000",
  "status": 400,
  "error": "Bad Request",
  "errors": [
    {
      "codes": [
        "NotEmpty.userVo.password",
        "NotEmpty.password",
        "NotEmpty.java.lang.String",
        "NotEmpty"
      ],
      "arguments": [
        {
          "codes": [
            "userVo.password",
            "password"
          ],
          "arguments": null,
          "defaultMessage": "password",
          "code": "password"
        }
      ],
      "defaultMessage": "不能为空",
      "objectName": "userVo",
      "field": "password",
      "rejectedValue": "",
      "bindingFailure": false,
      "code": "NotEmpty"
    },
    {
      "codes": [
        "NotEmpty.userVo.username",
        "NotEmpty.username",
        "NotEmpty.java.lang.String",
        "NotEmpty"
      ],
      "arguments": [
        {
          "codes": [
            "userVo.username",
            "username"
          ],
          "arguments": null,
          "defaultMessage": "username",
          "code": "username"
        }
      ],
      "defaultMessage": "不能为空",
      "objectName": "userVo",
      "field": "username",
      "rejectedValue": "",
      "bindingFailure": false,
      "code": "NotEmpty"
    }
  ],
  "message": "Validation failed for object='userVo'. Error count: 2",
  "path": "/study-web/save2"
}
  1. 访问该地址http://localhost:8989/study-web/swagger-ui.html,找到user-controllerupdate2接口,按下图,配置参数,点击Try it out按钮
    简单实现spring web工程下的请求参数校验_第9张图片
  2. 会得到如下的响应结果,说明分组验证功能成功,修改是是idusername不能为空
{
  "timestamp": "2019-06-27T02:40:20.497+0000",
  "status": 400,
  "error": "Bad Request",
  "errors": [
    {
      "codes": [
        "NotNull.userVo.id",
        "NotNull.id",
        "NotNull.java.lang.Integer",
        "NotNull"
      ],
      "arguments": [
        {
          "codes": [
            "userVo.id",
            "id"
          ],
          "arguments": null,
          "defaultMessage": "id",
          "code": "id"
        }
      ],
      "defaultMessage": "不能为null",
      "objectName": "userVo",
      "field": "id",
      "rejectedValue": null,
      "bindingFailure": false,
      "code": "NotNull"
    },
    {
      "codes": [
        "NotEmpty.userVo.username",
        "NotEmpty.username",
        "NotEmpty.java.lang.String",
        "NotEmpty"
      ],
      "arguments": [
        {
          "codes": [
            "userVo.username",
            "username"
          ],
          "arguments": null,
          "defaultMessage": "username",
          "code": "username"
        }
      ],
      "defaultMessage": "不能为空",
      "objectName": "userVo",
      "field": "username",
      "rejectedValue": "",
      "bindingFailure": false,
      "code": "NotEmpty"
    }
  ],
  "message": "Validation failed for object='userVo'. Error count: 2",
  "path": "/study-web/update2"
}

自定义检验

手动检验

关于自定义校验功能和在代码层面手动调用校验功能,请参考西面的连接:
使用spring validation完成数据后端校验

你可能感兴趣的:(spring,web)