当涉及校验用户输入时,如果由开发人员编码校验数据,那么验证数据的代码和业务逻辑代码会耦合在一起。Spring Framework 4.0支持Bean Validation 1.0(JSR-303)和Bean Validation 1.1(JSR-349),也将其改写成了Spring的Validator接口。使用Spring Validation可以方便的进行数据校验。

1.引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2.实体类中添加校验注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package io.github.lmy.springbootinaction.request;

import io.github.lmy.springbootinaction.entity.User;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import org.springframework.beans.BeanUtils;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

@Data
public class UserRequest {

@NotBlank(message = "用户名不能为空")
private String name;

@Range(min = 0, max = 150, message = "年龄格式不正确")
@NotNull
private Integer age;

@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;

@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$", message = "手机号码格式不正确")
@NotBlank(message = "手机号不能为空")
private String phone;

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

}

3.在Controller层校验数据

对需要校验的参数添加注解@Valid或@Validated,如果需要获取校验的信息,可以在被校验参数后面添加一个BindingResult参数,Spring会将校验的信息填充到该校验参数后面的BindingResult中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package io.github.lmy.springbootinaction.controller;

import io.github.lmy.springbootinaction.request.LoginRequest;
import io.github.lmy.springbootinaction.request.UserRequest;
import io.github.lmy.springbootinaction.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.Valid;

@Controller
@RequestMapping("/user")
@Slf4j
public class UserController {

@Autowired
private UserService userService;

@PostMapping("/register")
@ResponseBody
public boolean register(@Valid @RequestBody UserRequest userRequest, BindingResult result) {
if (result.hasErrors()) {
for (FieldError fieldError : result.getFieldErrors()) {
log.info("field: {}, message: {}", fieldError.getField(), fieldError.getDefaultMessage());
}
return false;
}
boolean success = userService.register(userRequest);
return success;
}

@PostMapping("/login")
public String login(@Validated @RequestBody LoginRequest loginRequest) {
userService.login(loginRequest);
return "index";
}

}

4.校验失败统一处理

如果校验参数后没有BindingResult参数,且校验参数校验失败,则会抛出MethodArgumentNotValidException 异常。可以使用 @ExceptionHandler 注解统一处理参数校验失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package io.github.lmy.springbootinaction.global;

import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Map;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> result = ex.getBindingResult()
.getFieldErrors()
.stream()
.collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
return result;
}

}

参考链接: