前端校驗易被繞過,後端校驗是數據安全的最後防線。SpringMVC 基於 JSR-303 (Bean Validation) 標準,提供 @Valid 註解實現優雅的參數校驗,配合 BindingResult 即可獲取錯誤詳情。
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.apache.bval</groupId>
<artifactId>bval-jsr303</artifactId>
<version>0.5</version>
</dependency>
public class PersonScope implements Serializable {
private String name;
private Integer age;
private String address;
// getter/setter 省略
}
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
public class PersonalValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return PersonScope.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "name", null, "姓名不能為空!");
PersonScope person = (PersonScope) target;
if (person.getAddress() == null || person.getAddress().trim().isEmpty()) {
errors.rejectValue("address", null, "家庭地址不能為空!!!!");
}
}
}
@Controller
public class TestPersonalValidatorController {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(new PersonalValidator());
}
@RequestMapping("testPersonalValidator.do")
@ResponseBody
public Object test(@Valid PersonScope person, BindingResult result) {
if (result.hasErrors()) {
StringBuilder sb = new StringBuilder();
for (ObjectError error : result.getAllErrors()) {
sb.append(((FieldError) error).getField())
.append(" : ")
.append(error.getDefaultMessage());
}
return sb.toString();
}
return person;
}
}
關鍵點:@Valid 必須緊貼參數(@Valid PersonScope person),BindingResult 必須緊隨其後。
<mvc:annotation-driven validator="personalValidator">
<bean id="personalValidator" class="com.example.validator.PersonalValidator"/>
</mvc:annotation-driven>
此方式無需在每個 Controller 添加 @InitBinder,自動對所有標註 @Valid 的參數生效。
實體類直接加註解,無需寫 Validator 實現類:
public class PersonScope {
@NotBlank(message = "姓名不能為空")
private String name;
@Min(value = 0, message = "年齡不能為負數")
@Max(value = 150, message = "年齡超出合理範圍")
private Integer age;
@NotBlank(message = "地址不能為空")
@Size(max = 200, message = "地址長度不能超過200字符")
private String address;
// getter/setter
}
常用註解:@NotNull、@NotBlank、@Size、@Min/@Max、@Pattern、@Email 等。
if (bindingResult.hasErrors()) {
Map<String, String> errors = new HashMap<>();
bindingResult.getFieldErrors().forEach(e ->
errors.put(e.getField(), e.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}
<%@ taglib prefix="form" uri="https://www.springframework.org/tags/form" %>
<form:form modelAttribute="personScope">
姓名: <form:input path="name"/> <form:errors path="name" cssClass="error"/><br/>
地址: <form:input path="address"/> <form:errors path="address" cssClass="error"/>
</form:form>

同一實體在不同場景(新增/修改)需不同校驗規則:
public interface AddGroup {} // 新增分組
public interface UpdateGroup {} // 修改分組
public class User {
@NotNull(groups = UpdateGroup.class, message = "ID 不能為空")
private Long id;
@NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "用戶名不能為空")
private String username;
}
// Controller 指定分組
@PostMapping("add")
public Result add(@Validated(AddGroup.class) @RequestBody User user) { ... }
@PutMapping("update")
public Result update(@Validated(UpdateGroup.class) @RequestBody User user) { ... }
SpringMVC 數據校驗三大路徑:
建議:常規字段校驗用註解,複雜業務規則用自定義 Validator,配合分組校驗滿足多場景需求。