前言
本篇文章的主要目的是模拟几种常见的在springmvc的controller层使用数据校验的情形,观察不同情形下校验出错时抛出的异常类型。
数据对象User
@Data
public class User {
@NotBlank
private String username;
/**
* 如果只设置@Size,当password是null的时候,不会对@Size进行判断
*/
@NotNull
@Size(min = 6, max = 20)
private String password;
}
检测后台接收数据情况
先不开启校验,检测数据接收情况:
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/login")
public User test(User user) {
return user;
}
@PostMapping("/login")
public User login(User user) {
return user;
}
}
使用get+query params或content-type multipart/form-data的方式时,后端都能把数据绑定给user:
通过get+content-type application/x-www-form-urlencoded或者get+content-type application/json时,后端无法将数据绑定给user:
如果使用post,则query params、form-data和urlencoded3种方法都能让后端把数据绑给user,传json不行。
如果使用post传json,后端login方法的user参数必须加上@RequestBody注解,这样参数才能绑给user。此时post只能传json,用其他3种方式都会抛出HttpMediaTypeNotSupportedException异常。
所以得出结论,当后端方法上的user参数没有@RequestBody注解时,get只能使用query params和form-data两种方式传参,post可以使用query params、form-data和urlencoded三种方式传参。
只用@Validated校验
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/login")
public User test(@Validated User user) {
return user;
}
@PostMapping("/login")
public User login(@Validated User user) {
return user;
}
}
只使用@Validated注解时,当使用上面5种有效的传参方式传递不符合校验条件的参数时,都收到400 bad request,后端抛出BindException异常。使用其他无效的传参方式时,即使传递了符合校验条件的参数,但是由于后端无法将参数绑定给user,所以仍然抛出BindException。
使用@RequestBody+@Validated
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/login")
public User test(@RequestBody @Validated User user) {
return user;
}
@PostMapping("/login")
public User login(@RequestBody @Validated User user) {
return user;
}
}
使用get+query params、form-data和urlencoded时,都返回400 bad request,后端抛出HttpMessageNotReadableException。使用get+json传递不符合校验条件的参数时,返回400 bad request,后端抛出MethodArgumentNotValidException异常。
使用get+json传递符合校验条件的参数时,user对象成功返回。
使用post+query params传递参数时,返回400 bad request,后端抛出HttpMessageNotReadableException异常。
使用post+form-data或者urlencoded传递参数时,返回415 not supported media type,后端抛出HttpMediaTypeNotSupportedException异常。
使用post+json传递不符合校验条件的参数时,返回MethodArgumentNotValidException异常。
使用post+json传递符合校验条件的参数时,user对象成功返回。
由于后端没有针对上面3种异常处理,所以默认会由DefaultHandlerExceptionResolver处理。DefaultHandlerExceptionResolver会根据异常返回对应的错误码,然后打印一条warn级别的日志。
不需使用User对象接收数据
@Validated
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/register")
public User test(@NotBlank String username, @NotNull @Size(min = 6, max = 20) String password) {
return new User().setUsername(username).setPassword(password);
}
@PostMapping("/register")
public User register(@NotBlank String username, @NotNull @Size(min = 6, max = 20) String password) {
return new User().setUsername(username).setPassword(password);
}
}
直接在参数中使用username和password,不论是因为使用不合法的传参方式导致后端没接收到参数,还是使用5种合法的传参方式但是传递不符合校验条件的参数,都返回500 internal server error,后端都会抛出ConstraintViolationException。spring默认的HandlerExceptionResolver都无法处理ConstraintViolationException,所以后端会打印异常,而不是warn级别的日志。
总结
前端传参要符合规范,使用get的时候,只用query params传参,不要用body。使用post的时候,用body,不要用query params。
后端使用User对象接收参数时:
- user参数使用@Validated注解。
- 如果是get方法,使用query params传参,参数不符合校验规则时,抛出BindException。
- 如果是post方法,使用form-data和urlencoded传参时,参数不符合校验规则时,抛出BindException。使用json传参时(此时user参数需要加上@RequestBody注解),参数不符合校验规则时,抛出MethodArgumentNotValidException异常。
后端用两个String接收参数时:
-
Controller类上使用@Validated注解。
-
get使用query params传参,post使用form-data和urlencoded传参,参数不符合校验规则时,都抛出ConstraintViolationException异常。
DefaultHandlerExceptionResolver可以处理BindException和MethodArgumentNotValidException,所以抛这两个异常的时候,不会打印异常栈,只会打印一条warn级别的日志,并且返回400 bad request。DefaultHandlerExceptionResolver不处理ConstraintViolationException,所以抛这个异常的时候,会打印异常栈,并且返回500 internal exception。