springmvc中的数据校验

945 阅读4分钟

前言

本篇文章的主要目的是模拟几种常见的在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:

image-20210109004832954

image-20210109004932543

通过get+content-type application/x-www-form-urlencoded或者get+content-type application/json时,后端无法将数据绑定给user:

image-20210109005710930

image-20210109005839973

如果使用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。