提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
@Validation是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置Validation可以很轻松的完成对数据的约束。
错误的状态码:返回的响应码推荐使用400 bad request.
说明:若不做异常处理,@Validated注解的默认异常消息如下(示例):
2020-09-05 21:48:38.106 WARN 9796 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver :
Resolved [org.springframework.web.bind.MethodArgumentNotValidException:
Validation failed for argument [0] in public java.lang.String com.example.validateddemo.controller.DemoController.validatedDemo1(com.example.validateddemo.entity.dto.UseDto):
[Field error in object 'useDto' on field 'username': rejected value [null];
codes [NotBlank.useDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [useDto.username,username]; arguments [];
default message [username]]; default message [用户名不能为空!]] ]
package com.example.validateddemo.handler;
import com.example.validateddemo.base.Result;
import com.example.validateddemo.enums.ResultEnum;
import com.example.validateddemo.utils.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.List;
@Slf4j
@ControllerAdvice
public class ValidatedExceptionHandler {
/**
* 处理@Validated参数校验失败异常
* @param exception 异常类
* @return 响应
*/
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result exceptionHandler(MethodArgumentNotValidException exception){
BindingResult result = exception.getBindingResult();
StringBuilder stringBuilder = new StringBuilder();
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
if (errors != null) {
errors.forEach(p -> {
FieldError fieldError = (FieldError) p;
log.warn("Bad Request Parameters: dto entity [{}],field [{}],message [{}]",fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());
stringBuilder.append(fieldError.getDefaultMessage());
});
}
}
return ResultUtil.validatedException(stringBuilder.toString());
}
}
Spring Boot–09–注解@Valid
@Valid级联校验
级联校验: 也叫嵌套检测.嵌套就是一个实体类包含另一个实体类
@Valid和可以用在成员属性的字段上,因此 @Valid可以提供级联校验
@Data
public class Hair {
@NotBlank(message = "头发长度必须提交!")
private Double length;
@NotBlank(message = "头发颜色必须提交!")
private String color;
}
@Data
public class Person {
@NotBlank(message = "用户姓名必须提交!")
@Size(min=2, max=8)
private String userName;
// 添加@Valid注解实现嵌套检测
@Valid
@NotEmpty(message = "用户要有头发!")
private List<Hair> hairs;
}
@PostMapping("/person")
public Result addPerson(@Valid @RequestBody Person person) {
return Result.buildSuccess(person);
}
只是在方法参数前面添加 @Valid和 @Validated注解,不会对嵌套的实体类进行校验.要想实现对嵌套的实体类进行校验,需要在嵌套的实体类属性上添加 @Valid注解
假设有这样一种场景:
我们使用同一个VO(Request)类来传递save和update方法的数据,
(1)创建分组接口
这里并不需要实现编写什么代码,标明分类。
//分组接口 1
public interface InsertGroup {
}
//分组接口 2
public class UpdateGroup {
}
(2)Request实体类
@Data
@NoArgsConstructor
public class TestRequest {
@Null(message = "无需传id",groups = InsertGroup.class)
@NotBlank(message = "必须传入id",groups = UpdateGroup.class)
private String id;
}
(3)controller接口
@RequestMapping("/valid/test")
public ResponseEntity<ResponseResult> test(@Validated({UpdateGroup.class})@RequestBody TestRequest request) {
System.out.println(request);
return null;
}
(4)postman测试
(5)注意事项!!!
当我们在controller层指定分组后,属性上没有表名分组的校验还执行么?
下面的Request实体中,id进行了分组,address没有进行分组:
@Data
@NoArgsConstructor
public class TestRequest {
@Null(message = "无需传id",groups = InsertGroup.class)
@NotBlank(message = "必须传入id",groups = UpdateGroup.class)
private String id;
@Length(max = 3, message = "address最大长度是3")
private String address;
}
postman测试:
如下图,校验没有成功!!!!说明一旦开启了分组校验,就必须把所有的校验规则都指定组别,不然不生效。
默认情况下,分组间的约束是无序的,但是在一些特殊的情况下可能对分组间的校验有一定的顺序
分组校验顺序通过使用 @GroupSequence注解实现
@Data
public class UserGroupSequence {
public interface FirstGroup {}
public interface SecondGroup {}
// 使用GroupSequence定义分组校验顺序:按照FirstGroup,SecondGroup分组顺序进行校验
@GroupSequence({FirstGroup.class, SecondGroup.class})
public interface Group {}
@NotEmpty(message = "用户ID必须提交!", group = FirstGroup.class)
private String userId;
@NotEmpty(message = "用户姓名必须提交!", group = FirstGroup.class)
@Size(min = 2, max = 8, message = "用户姓名的长度在2~8之间", goup = Second.class)
private String userName;
}
Controller
@RestController
@RequestMapping("/user")
public class UserGroupSequenceController {
// 这里方法中@Validated注解value的值是Group.class
@PostMapping("/user")
public Result addGroup(@Validated(value = Group.class) @RequestBody UserGroupSequence user) {
return Result.buildSuccess(user);
}
}
使用 @GroupSequence注解指定分组校验顺序后,第一组分组的约束的校验没有通过后,就不会进行第二组分组的约束的校验
在方法级别上使用@Validated注解,需要在方法参数上添加该注解。下面是一个简单的示例,演示了如何在方法级别上使用@Validated注解。
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public User createUser(@RequestBody @Validated User user) {
// 处理用户创建逻辑
}
}
在上面的示例代码中,@Validated注解被应用在User对象上,用于验证请求体中的数据是否符合要求。
在类级别上使用@Validated注解,需要在类上添加该注解。下面是一个简单的示例,演示了如何在类级别上使用@Validated注解。
@RestController
@RequestMapping("/users")
@Validated
public class UserController {
@PostMapping
public User createUser(@RequestBody User user) {
// 处理用户创建逻辑
}
}
在上面的示例代码中,@Validated注解被应用在UserController类上,用于验证该类中所有方法的参数是否符合要求。
get参数校验,需要在类上加@Validated
@RestController
@Slf4j
@RequestMapping("/api/test")
@Validated
public class TestController {
@GetMapping(value = "/h11")
public String test11( @NotEmpty(message = "姓名不能为空") String name) {
System.out.println("kaidsd");
System.out.println(name);
return "applyInfoDTO";
}
}