1. 概述
表单验证从来不像我们想象的那么简单。验证用户输入的数据对于维护数据完整性至关重要,这点毋庸置疑。
在 Web 应用中,数据输入通常通过 HTML 表单完成,需要同时进行客户端验证和服务端验证。
本教程将演示如何:
- ✅ 使用 AngularJS 实现客户端验证
- ✅ 使用 Spring MVC 框架实现服务端验证
⚠️ 本文聚焦 Spring MVC 实现。关于 Spring Boot 的验证方案可参考《Spring Boot 中的数据校验》。
2. Maven 依赖
首先添加核心依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
最新版本可从 Maven Central 下载:
3. 使用 Spring MVC 进行验证
永远不要只依赖客户端验证! 服务端验证是防止恶意数据或错误输入破坏系统的最后一道防线。
Spring MVC 通过 JSR 349 Bean Validation 规范注解支持服务端验证。本示例使用其参考实现 hibernate-validator。
3.1. 数据模型
创建 User
类,使用验证注解标记属性:
public class User {
@NotNull
@Email
private String email;
@NotNull
@Size(min = 4, max = 15)
private String password;
@NotBlank
private String name;
@Min(18)
@Digits(integer = 2, fraction = 0)
private int age;
// 标准构造器、getter/setter
}
除
@NotBlank
属于 hibernate-validator 扩展外,其余均为 JSR 349 标准注解。
3.2. Spring MVC 控制器
创建控制器类,定义 /user
接口保存新用户:
@PostMapping(value = "/user")
@ResponseBody
public ResponseEntity<Object> saveUser(@Valid User user,
BindingResult result, Model model) {
if (result.hasErrors()) {
List<String> errors = result.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return new ResponseEntity<>(errors, HttpStatus.OK);
} else {
if (users.stream().anyMatch(it -> user.getEmail().equals(it.getEmail()))) {
return new ResponseEntity<>(
Collections.singletonList("邮箱已存在!"),
HttpStatus.CONFLICT);
} else {
users.add(user);
return new ResponseEntity<>(HttpStatus.CREATED);
}
}
}
关键点:
@Valid
注解触发验证BindingResult
捕获验证错误- 额外检查邮箱唯一性(客户端无法完成)
初始化用户列表:
private List<User> users = Arrays.asList(
new User("ana@example.com", "pass", "Ana", 20),
new User("bob@example.com", "pass", "Bob", 30),
new User("john@example.com", "pass", "John", 40),
new User("mary@example.com", "pass", "Mary", 30));
添加获取用户列表的接口:
@GetMapping(value = "/users")
@ResponseBody
public List<User> getUsers() {
return users;
}
返回主页的映射:
@GetMapping("/userPage")
public String getUserProfilePage() {
return "user";
}
3.3. Spring MVC 配置
基础 MVC 配置:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.baeldung.springmvcforms")
class ApplicationConfiguration implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Bean
public InternalResourceViewResolver htmlViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setPrefix("/WEB-INF/html/");
bean.setSuffix(".html");
return bean;
}
}
3.4. 初始化应用
实现 WebApplicationInitializer
启动应用:
public class WebInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx
= new AnnotationConfigWebApplicationContext();
ctx.register(ApplicationConfiguration.class);
ctx.setServletContext(container);
container.addListener(new ContextLoaderListener(ctx));
ServletRegistration.Dynamic servlet
= container.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
}
}
3.5. 使用 cURL 测试验证
实现 AngularJS 前前,先用 cURL 测试 API:
curl -i -X POST -H "Accept:application/json"
"localhost:8080/spring-mvc-forms/user?email=aaa&password=12&age=12"
响应包含默认错误信息:
[
"不是有效的邮箱地址",
"长度需在4到15之间",
"不能为空",
"必须大于等于18"
]
4. AngularJS 验证
客户端验证能提升用户体验,提供即时反馈并减少无效请求。AngularJS 对表单验证有完善支持。
首先创建注入 ngMessages
模块的 AngularJS 模块:
var app = angular.module('app', ['ngMessages']);
4.1. AngularJS 服务
创建服务调用后端接口:
app.service('UserService',['$http', function ($http) {
this.saveUser = function saveUser(user){
return $http({
method: 'POST',
url: 'user',
params: {email:user.email, password:user.password,
name:user.name, age:user.age},
headers: 'Accept:application/json'
});
}
this.getUsers = function getUsers(){
return $http({
method: 'GET',
url: 'users',
headers:'Accept:application/json'
}).then( function(response){
return response.data;
} );
}
}]);
4.2. AngularJS 控制器
控制器处理业务逻辑和错误响应:
app.controller('UserCtrl', ['$scope','UserService', function ($scope,UserService) {
$scope.submitted = false;
$scope.getUsers = function() {
UserService.getUsers().then(function(data) {
$scope.users = data;
});
}
$scope.saveUser = function() {
$scope.submitted = true;
if ($scope.userForm.$valid) {
UserService.saveUser($scope.user)
.then (function success(response) {
$scope.message = '用户添加成功!';
$scope.errorMessage = '';
$scope.getUsers();
$scope.user = null;
$scope.submitted = false;
},
function error(response) {
if (response.status == 409) {
$scope.errorMessage = response.data.message;
}
else {
$scope.errorMessage = '添加用户失败!';
}
$scope.message = '';
});
}
}
$scope.getUsers();
}]);
关键点:
submitted
变量控制验证信息显示时机- 仅在表单有效时提交 (
$valid
) - 单独处理邮箱冲突错误 (HTTP 409)
4.3. AngularJS 表单验证
在 user.html
引入脚本:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js">
</script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-messages.js">
</script>
<script src="js/app.js"></script>
绑定模块和控制器:
<body ng-app="app" ng-controller="UserCtrl">
创建表单结构:
<form name="userForm" method="POST" novalidate
ng-class="{'form-error':submitted}" ng-submit="saveUser()" >
...
</form>
⚠️ 必须设置
novalidate
禁用 HTML5 原生验证
添加输入字段:
<label class="form-label">邮箱:</label>
<input type="email" name="email" required ng-model="user.email" class="form-input"/>
<label class="form-label">密码:</label>
<input type="password" name="password" required ng-model="user.password"
ng-minlength="4" ng-maxlength="15" class="form-input"/>
<label class="form-label">姓名:</label>
<input type="text" name="name" ng-model="user.name" ng-trim="true"
required class="form-input" />
<label class="form-label">年龄:</label>
<input type="number" name="age" ng-model="user.age" ng-min="18"
class="form-input" required/>
验证规则:
- HTML5
required
+ AngularJS 特性 (ng-minlength
,ng-min
等) - 邮箱字段使用
type="email"
使用 ng-messages
显示错误信息:
<div ng-messages="userForm.email.$error"
ng-show="submitted && userForm.email.$invalid" class="error-messages">
<p ng-message="email">无效的邮箱!</p>
<p ng-message="required">邮箱必填!</p>
</div>
显示逻辑:
- 仅在提交后显示 (
submitted && $invalid
) - 每次只显示一条错误
添加有效状态提示:
<div class="check" ng-show="userForm.email.$valid">✓</div>
样式增强:
.form-error input.ng-invalid {
border-color:red;
}
.error-messages {
color:red;
}
最终效果示例:
5. 总结
我们成功实现了:
- ✅ 服务端验证(Spring MVC + Bean Validation)
- ✅ 客户端验证(AngularJS + ngMessages)
- ✅ 双端验证协同工作
完整源代码见 GitHub 仓库。启动后访问 /userPage 查看效果。