验证表单输入(接口参数)是Web 开发 (API 接口开发)中不可缺少的一个部分。
@NotBlank Validation
首先我们从最简单的不能为空判断开始,介绍 Spring Boot Validation。
假设我们需要提供一个登陆接口,输入用户名密码,返回是否登陆成功。
在去数据库查询用户名密码是否正确之前,我们可以做一些简单的验证。
在 LoginRequest 的 username 和 password 参数上加上 @NotBlank。
package com.example.validation.request;
import javax.validation.constraints.NotBlank;
public class LoginRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
在 UserController::login() 方法接收输入的 loginRequest 前面加上 @Valid 。
package com.example.validation.controllers;
import com.example.validation.request.LoginRequest;
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.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/login")
public LoginRequest login(@RequestBody @Valid LoginRequest loginRequest) {
return loginRequest;
}
}
这样当用户输入的用户名或则密码为空的时候,就不会继续进入到下面的业务逻辑,在验证参数的阶段就可以直接返回 400 错误。
Bean Validation
使用 @NotBlank 完成第一个参数验证的例子的时候,会有这样一个问题:@NotBlank 注解是不是 Spring 框架自带的,这样的注解一共有多少个。
这个时候我们就需要了解一个 Bean Validation。

Bean Validation 是一套通过注解来表示对象模型的约束的 Java 规范,最新版本是 Bean Validation 2.0。
定义的内置约束注解有:

Hibernate Validator 是 Bean Validation 规范的实现,除了Bean Validation 定义的 22 种内置约束注解之外,还实现了另外 25 种内置约束注解。

Creating custom constraints
虽然 Hibernate Validator 已经内置了这么多约束注解,但是需求千变万化,总有不能满足的时候,这时候我们就需要定义并实现自己的一套约束注解。
假设我们需要提供一个注册接口,输入用户名、手机号码、邮箱、密码,返回是否注册成功。
除了需要验证用户名密码是否为空之外,还需要验证手机号码和邮箱至少要有一个是必填的。
MinimumRequiredParameters.java
package com.example.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = MinimumRequiredParametersValidator.class)
@Documented
public @interface MinimumRequiredParameters {
String message() default "{minimum.required.parameters.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] value();
int minimum() default 1;
}
MinimumRequiredParametersValidator.java
package com.example.validation;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MinimumRequiredParametersValidator implements ConstraintValidator<MinimumRequiredParameters, Object> {
private String[] fields;
private int minimum;
private int count;
@Override
public void initialize(MinimumRequiredParameters constraintAnnotation) {
this.fields = constraintAnnotation.value();
this.minimum = constraintAnnotation.minimum();
this.count = 0;
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(value);
for (String field: fields) {
if (wrapper.getPropertyValue(field) != null) { count++; }
}
return count >= minimum;
}
}
resources/ValidationMessages.properties
minimum.required.parameters.message=at least {minimum} of {value} is required
在 RegisterRequest 中使用 @MinimumRequiredParameters 注解判断手机号码和邮箱至少有一个必填。
package com.example.validation.request;
import com.example.validation.MinimumRequiredParameters;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@MinimumRequiredParameters(value = {"phone", "email"}, message = "手机号码和邮箱至少有一个必填")
public class RegisterRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@Pattern(regexp = "\\d{11}", message = "手机号码格式不正确")
private String phone;
@Email(message = "邮箱格式不正确")
private String email;
@NotBlank(message = "密码不能为空")
private String password;
public String getUsername() {
return username;
}
public String getPhone() {
return phone;
}
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
}
参考:
https://spring.io/guides/gs/validating-form-input/
https://beanvalidation.org/2.0/spec/#builtinconstraints
http://hibernate.org/validator/
663 total views, 1 views today