Spring基础知识(15)- Spring MVC (五) | 表单标签库、数据校验
1. 表单标签库
进行 Spring MVC 项目开发时,一般会使用 EL 表达式和 JSTL 标签来完成页面视图的开发。其实 Spring 也有自己的一套表单标签库,通过 Spring 表单标签,可以很容易地将模型数据中的命令对象绑定到 HTML 表单元素中。
和 JSTL 标签的使用方法相同,在使用 Spring 表单标签之前,必须在 JSP 页面开头处声明 taglib 指令,指令代码如下。
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="fm" %>
常用的 Spring 表单标签如下表所示。
名称 | 作用 |
form | 渲染表单元素 |
input | 输入框组件标签,渲染 <input type="text"/> 元素 |
password | 密码框组件标签,渲染 <input type="password"/> 元素 |
hidden | 隐藏框组件标签,渲染 <input type="hidden"/> 元素 |
textarea | 多行输入框组件标签,渲染 textarea 元素 |
checkbox | 复选框组件标签,渲染一个 <input type="checkbox"/> 元素 |
checkboxes | 渲染多个 <input type="checkbox"/> 元素 |
radiobutton | 单选框组件标签,渲染一个 <input type="radio"/> 元素 |
radiobuttons | 渲染多个 <input type="radio"/> 元素 |
select | 下拉列表组件标签,渲染一个选择元素 |
option | 渲染一个选项元素 |
options | 渲染多个选项元素 |
errors | 显示表单数据校验所对应的错误信息 |
以上标签基本都拥有以下属性。
path:属性路径,表示表单对象属性,如 userName、userCode 等。
cssClass:表单组件对应的 CSS 样式类名。
cssErrorClass:当提交表单后报错(服务端错误),采用的 CSS 样式类。
cssStyle:表单组件对应的 CSS 样式。
htmlEscape:绑定的表单属性值是否要对 HTML 特殊字符进行转换,默认为 true。
示例
在 “Spring基础知识(12)- Spring MVC (二)” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。
(1) 使用 src/main/java/com/example/entity/User.java 文件
1 package com.example.entity; 2 3 public class User { 4 private int id; 5 private String username; 6 private String password; 7 8 public User() { 9 10 } 11 12 public int getId() { 13 return id; 14 } 15 16 public void setId(int id) { 17 this.id = id; 18 } 19 20 public String getUsername() { 21 return this.username; 22 } 23 24 public void setUsername(String username) { 25 this.username = username; 26 } 27 28 public String getPassword() { 29 return password; 30 } 31 32 public void setPassword(String password) { 33 this.password = password; 34 } 35 }
(2) View
创建 src/main/webapp/WEB-INF/jsp/user_add.jsp 文件
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> 2 <%@ taglib uri="http://www.springframework.org/tags/form" prefix="fm" %> 3 <html> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 6 <title>Add User</title> 7 </head> 8 <body> 9 10 <h4>Add User</h4> 11 <fm:form method="POST" modelAttribute="user" action="${pageContext.request.contextPath }/user/show"> 12 <table> 13 <tr> 14 <td><fm:label path="username">Username:</fm:label></td> 15 <td><fm:input path="username" value="admin" /></td> 16 </tr> 17 <tr> 18 <td><fm:label path="password">Password:</fm:label></td> 19 <td><fm:input path="password" value="123456" /></td> 20 </tr> 21 <tr> 22 <td colspan="2"> 23 <input type="submit" value="Submit"/> 24 </td> 25 </tr> 26 </table> 27 </fm:form> 28 29 </body> 30 </html>
<fm:form> 标签的 modelAttribute 属性用于指定绑定的模型属性。默认从模型中尝试取名为 “command” 的表单对象,若不存在此表单对象,将会报错。所以一般情况下会指定 modelAttribute 属性。
此外,表单组件标签页拥有 HTML 标签的各种属性,如 id、onclick 等,都可以根据需要灵活使用。
创建 src/main/webapp/WEB-INF/jsp/user_show.jsp 文件
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>Show User</title> 6 </head> 7 <body> 8 <p>Your account info: </p> 9 <!-- 使用 EL 表达式取出 model 中的 user 信息 --> 10 <p>Username: ${user.username }</p> 11 <p>Password: ${user.password }</p> 12 </body> 13 </html>
(3) 修改 src/main/java/com/example/controller/UserController.java 文件
1 package com.example.controller; 2 3 import org.springframework.stereotype.Controller; 4 import org.springframework.web.bind.annotation.ModelAttribute; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RequestMethod; 7 8 import com.example.entity.User; 9 10 @Controller 11 @RequestMapping("/user") 12 public class UserController { 13 14 @RequestMapping(value = "/add", method = RequestMethod.GET) 15 public String userAdd(@ModelAttribute("user") User user) { 16 return "user_add"; 17 } 18 19 @RequestMapping(value = "/show", method = RequestMethod.POST) 20 public String userShow(User user) { 21 return "user_show"; 22 } 23 24 ... 25 }
访问:http://localhost:9090/user/add
2. 数据校验
一般情况下,用户的输入是随意的,为了保证数据的合法性,数据验证是所有 Web 应用必须处理的问题。
Spring MVC 有以下两种方法可以验证输入:
(1) 利用 Spring 自带的验证框架
(2) 利用 JSR 303 实现
数据验证分为客户端验证和服务器端验证,客户端验证主要是过滤正常用户的误操作,通过 JavaScript 代码完成。服务器端验证是整个应用阻止非法数据的最后防线,通过在应用中编程实现。本文使用 JSR 303 实现服务器端的数据验证。
JSR 303 是 Java 为 Bean 数据合法性校验所提供的标准框架。JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。可以通过 https://jcp.org/en/jsr/detail?id=303 查看详细内容并下载 JSR 303 Bean Validation。
JSR 303 不需要编写验证器,它定义了一套可标注在成员变量、属性方法上的校验注解,如下表所示。
名称 | 说明 |
@Null | 被标注的元素必须为 null |
@NotNull | 被标注的元素必须不为 null |
@AssertTrue | 被标注的元素必须为 true |
@AssertFalse | 被标注的元素必须为 false |
@Min(value) | 被标注的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被标注的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMax(value) | 被标注的元素必须是一个数字,其值必须大于等于指定的最大值 |
@DecimalMin(value) | 被标注的元素必须是一个数字,其值必须小于等于指定的最小值 |
@size | 被标注的元素的大小必须在指定的范围内 |
@Digits(integer,fraction) | 被标注的元素必须是一个数字,其值必须在可接受的范围内;integer 指定整数精度,fraction 指定小数精度 |
@Past | 被标注的元素必须是一个过去的日期 |
@Future | 被标注的元素必须是一个将来的日期 |
@Pattern(value) | 被标注的元素必须符合指定的正则表达式 |
Spring MVC 支持 JSR 303 标准的校验框架,Spring 的 DataBinder 在进行数据绑定时,可同时调用校验框架来完成数据校验工作,非常简单方便。在 Spring MVC 中,可以直接通过注解驱动的方式来进行数据校验。
Spring 本身没有提供 JSR 303 的实现,Hibernate Validator 实现了 JSR 303,所以必须在项目中加入 Hibernate Validator 库。
示例
在 “Spring基础知识(12)- Spring MVC (二)” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。
(1) 导入 validation-api, hibernate-validator 依赖包
访问 http://www.mvnrepository.com/,查询 validation-api, hibernate-validator
修改 pom.xml:
<project ... > ... <dependencies> ... <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.1.0.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.validation/validation-api --> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> ... </dependencies> ... </project>
在IDE中项目列表 -> SrpingmvcBasic -> 点击鼠标右键 -> Maven -> Reload Project
(2) 创建 src/main/java/com/example/entity/User.java 文件
1 package com.example.entity; 2 3 import javax.validation.constraints.NotNull; 4 import org.hibernate.validator.constraints.Email; 5 import org.hibernate.validator.constraints.Length; 6 7 public class User { 8 private int id; 9 @NotNull 10 @Length(min=2, max=8, message="Username 2 < length < 8") 11 private String username; 12 @NotNull(message="Password is required") 13 private String password; 14 @Email(regexp="[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]", message="Invalid email format") 15 private String email; 16 17 public User() { 18 19 } 20 21 public int getId() { 22 return id; 23 } 24 25 public void setId(int id) { 26 this.id = id; 27 } 28 29 public String getUsername() { 30 return this.username; 31 } 32 33 public void setUsername(String username) { 34 this.username = username; 35 } 36 37 public String getPassword() { 38 return password; 39 } 40 41 public void setPassword(String password) { 42 this.password = password; 43 } 44 45 public String getEmail() { 46 return email; 47 } 48 49 public void setEmail(String email) { 50 this.email = email; 51 } 52 }
(3) 创建 src/main/webapp/WEB-INF/jsp/user_add2.jsp 文件
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>Add User (2)</title> 6 </head> 7 <body> 8 9 <h4>Add User (2)</h4> 10 <form method="POST" action="${pageContext.request.contextPath }/user/add2/post"> 11 <table> 12 <tr> 13 <td>Username:</td> 14 <td><input name="username" value="admin" /></td> 15 </tr> 16 <tr> 17 <td>Password:</td> 18 <td><input name="password" value="123456" /></td> 19 </tr> 20 <tr> 21 <td>Email:</td> 22 <td><input name="email" value="username@test.com" /></td> 23 </tr> 24 <tr> 25 <td colspan="2"> 26 <input type="submit" value="Submit"/> 27 </td> 28 </tr> 29 </table> 30 </form> 31 32 </body> 33 </html>
(4) 修改 src/main/java/com/example/controller/UserController.java 文件
1 package com.example.controller; 2 3 import java.util.List; 4 import javax.validation.Valid; 5 6 import org.springframework.validation.BindingResult; 7 import org.springframework.validation.ObjectError; 8 9 import org.springframework.stereotype.Controller; 10 import org.springframework.ui.Model; 11 import org.springframework.web.bind.annotation.RequestMapping; 12 import org.springframework.web.bind.annotation.RequestMethod; 13 14 import com.example.entity.User; 15 16 @Controller 17 @RequestMapping("/user") 18 public class UserController { 19 20 @RequestMapping(value = "/add2", method = RequestMethod.GET) 21 public String add2() { 22 return "user_add2"; 23 } 24 25 @RequestMapping(value = "/add2/post", method = RequestMethod.POST) 26 public String add2Post(@Valid User user, BindingResult result, Model model) { 27 28 if (result.hasErrors()) { 29 List<ObjectError> errors = result.getAllErrors(); 30 31 String str = "<br>"; 32 for (ObjectError error : errors) { 33 str += error.getDefaultMessage(); 34 str += "<br>"; 35 } 36 37 model.addAttribute("message", str); 38 return "failed"; 39 } else { 40 model.addAttribute("message", "Add user success"); 41 return "success"; 42 } 43 } 44 45 }
访问:http://localhost:9090/user/add2