使用JSP处理表单
Spring MVC通过自定义JSP表单标签库提供了表单处理方法,要想使用该库内的标签,需要在JSP文件中添加该标签库的定义
<%@taglib uri="http://www.springframework.org/tags/form" prefix="mvc" %>
此处把mvc定义为标签库的前缀,使用标签库内标签时遵循<mvc:tagname>的格式
下用一个处理用户注册表单的例子来示范这些标签的使用
首先使用maven创建webapp工程,将servlet版本调整至3.1,工程最终目录结构如下
pom.xml配置上一节已经讲过,现在继续先在web.xml中定义DispatcherServlet。
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.mvc</url-pattern>
</servlet-mapping>
</web-app>
然后在springmvc-servlet.xml文件中配置好DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.lonelyquantum.wileybookch3" />
<context:annotation-config />
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
储存数据所用的User类以及存放性别数据使用的Gender枚举如下
public class User {
private String name;
private String lastname;
private String password;
private String detail;
@DateTimeFormat(pattern="yyyy-MM-dd")
private LocalDate birthDate;
private Gender gender;
private String country;
private boolean nonSmoking;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public boolean isNonSmoking() {
return nonSmoking;
}
public void setNonSmoking(boolean nonSmoking) {
this.nonSmoking = nonSmoking;
}
}
public enum Gender {
Male,
Female,
Other,
RefuseToAnswer;
}
此处为了照顾性少数者,性别栏添加了Other和RefuseToAnswer两项
然后就可以创建作为控制器的UserController类了。
@Controller
public class UserController {
private static final String[] countries = { "Turkey", "United States", "Germany", "China", "Japan", "The United Kingdom" };
@RequestMapping(value = "/form")
public ModelAndView user() {
ModelAndView modelAndView = new ModelAndView("userForm", "user", new User());
modelAndView.addObject("genders", Gender.values());
modelAndView.addObject("countries", countries);
return modelAndView;
}
@RequestMapping(value = "/result")
public ModelAndView processUser(User user) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("userResult");
modelAndView.addObject("u", user);
return modelAndView;
}
}
添加了几个国家~
两个方法分别返回注册界面和注册结果界面,下面就是这两个界面的JSP文件
注册界面userForm.jsp
<%@ page contentType="text/html; charset=ISO-8859-1" %>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="mvc" %>
<html>
<head>
<title>Spring MVC Form Handling</title>
</head>
<body>
<h2>User Registration Form</h2>
<mvc:form modelAttribute="user" action="result.mvc">
<table>
<tr>
<td><mvc:label path="name">Name</mvc:label></td>
<td><mvc:input path="name" /></td>
</tr>
<tr>
<td><mvc:label path="lastname">Last Name</mvc:label></td>
<td><mvc:input path="lastname" /></td>
</tr>
<tr>
<td><mvc:label path="password">Password</mvc:label></td>
<td><mvc:password path="password" /></td>
</tr>
<tr>
<td><mvc:label path="detail">Detail</mvc:label></td>
<td><mvc:textarea path="detail" /></td>
</tr>
<tr>
<td><mvc:label path="birthDate">Birth Date</mvc:label></td>
<td><mvc:input path="birthDate" /></td>
</tr>
<tr>
<td><mvc:label path="gender">Gender</mvc:label></td>
<td><mvc:radiobuttons path="gender" items="${genders}" /></td>
</tr>
<tr>
<td><mvc:label path="country">Country</mvc:label></td>
<td><mvc:select path="country" items="${countries}" /></td>
</tr>
<tr>
<td><mvc:label path="nonSmoking">Non Smoking</mvc:label></td>
<td><mvc:checkbox path="nonSmoking" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
</mvc:form>
</body>
</html>
首先通过<mvc:form>标签创建一个表单,在其中定义表单内的元素.该标签的modelAttribute特性向内部标签公开了一个绑定路径,该特性所指定的模型类将向视图公开,表单内标签的path特性将指向该模型类的属性,如name,lastname,gender等。而它的action特性则表示表单提交后跳转至的URL。
表单中用<mvc:input>标签定义了输入框,用于姓名、密码、生日的输入。
<mvc:textarea>标签定义了输入文本区域,用于详细介绍的输入。
<mvc:radiobutton>定义了选择性别的一系列单选按钮,与User中的gender属性对应,接受名为genders的数据作为选项的输入。
<mvc:select>定义了选择国家的下拉栏,与User中的country属性对应,接受名为countries的数据作为选项输入。
<mvc:checkbox>定义的复选框用于勾选是否吸烟。
以上标签之前都有<mvc:label>标签来给栏目进行说明。
最后用<input>(HTML的标签)定义了表单提交的按钮。
接下来,类似地创建userResult.jsp来显示用户输入的注册信息
<%@ page contentType="text/html; charset=ISO-8859-1" %>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="mvc" %>
<html>
<head>
<title>Spring MVC Form Handling</title>
</head>
<body>
<h2>User Registration Result</h2>
<table>
<tr>
<td>Name</td>
<td>${u.name}</td>
</tr>
<tr>
<td>Last name</td>
<td>${u.lastname}</td>
</tr>
<tr>
<td>Password</td>
<td>${u.password}</td>
</tr>
<tr>
<td>Detail</td>
<td>${u.detail}</td>
</tr>
<tr>
<td>Birth Date</td>
<td>${u.birthDate}</td>
</tr>
<tr>
<td>Gender</td>
<td>${u.gender}</td>
</tr>
<tr>
<td>Country</td>
<td>${u.country}</td>
</tr>
<tr>
<td>Non-Smoking</td>
<td>${u.nonSmoking}</td>
</tr>
</table>
</body>
</html>
注意此时从UserController传入视图的是一个被命名为“u”的User实例,在视图中通过${u.xxx}来获得其属性。
将项目部署到Tomcat服务器并运行,在浏览器中输入http://localhost:8080/FormRegister/form.mvc得到如下页面
单击Submit按钮跳转至信息显示页面
使用注解的强大功能
刚才的例子中已经用到了一系列注解,此处将解释他们的作用
- @Controller:表示被注解的类为MVC框架的一个Controller,DispatcherServlet扫描被@Controller注解的类并将Web请求映射到其被相应@RequestMapping注解的方法上
- @RequestMapping:标记用来将用户请求映射到处理器的类或者方法,可在类或方法级别使用,Value属性代表被映射的URL值的一部分
- @ModelAttribute:使用一个向视图公开的键将一个返回值和一个参数绑定,可以在方法或方法参数级别使用
- @PathVariable:将一个方法参数绑定到一个URL模板
- @ControllerAdvice:将代码集中,一遍跨控制器共享代码。使用该注解的类可以包含带有@ExceptionHandler、@InitBinder和@ModelAttribute注解的方法
- @InitBinder:确定用来初始化WebDataBinder的方法
- @ExceptionHandler:确定处理控制器类中发生的异常的方法
验证用户输入
在域类的属性上添加如下注解可以定义他们应当通过的验证:
- @Size注解定义长度
- @Email注解根据e-mail的正则表达式来验证输入
- @CreditCardNumber注解根据Luhn算法验证输入数字
- @Pattern注解根据其regexp属性定义的正则表达式来验证输入
我们在User类中用以上注解来规定几个属性的限制条件
public class User {
@Size(min=3, max=20,message = "error.username")
String username;
@Email
String email;
@CreditCardNumber(message = "error.ccNumber")
String ccNumber;
@Pattern(regexp = "^[a-zA-Z]\\w{3,14}$",message = "error.password")
String password;
//getters and setters
}
启动验证需要在pom.xml中添加如下依赖
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
</dependency>
为了在输入注册信不满足条件的时候返回注册界面显示错误信息,processUser方法需要进行修改
@Controller
public class UserController {
@RequestMapping(value = "/form")
public ModelAndView user() {
return new ModelAndView("userForm", "user", new User());
}
@RequestMapping(value = "/result", method= RequestMethod.POST)
public ModelAndView processUser(@Valid User user, BindingResult result) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("u", user);
if (result.hasErrors()) {
modelAndView.setViewName("userForm");
}
else {
modelAndView.setViewName("userResult");
}
return modelAndView;
}
}
其中的@Valid注解用于触发对user参数的验证,且方法接受一个BindingResult实例作为输入参数来检查验证是否有误,有误则返回添加了错误信息的注册界面
为了配置验证,还要在Web上下文中添加LocalValidatorFactoryBean和ReloadableResourceBundleMessageSource的定义,以及在mvc命名空间的annotation-driven标签中定义验证程序
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.lonelyquantum.wileybookch3" />
<context:annotation-config />
<mvc:annotation-driven validator="validator" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource"/>
</bean>
</beans>
加入了Bean验证后的userForm.jsp文件如下
<%@ page contentType="text/html; charset=ISO-8859-1" %>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="mvc" %>
<html>
<head>
<title>Spring MVC Form Validation</title>
<style type="text/css">
.formFieldError { background-color: #FFC; }
</style>
</head>
<body>
<h2>User Registration Form</h2>
<mvc:form modelAttribute="user" action="result.mvc">
<table>
<tr>
<td><mvc:label path="username">User Name</mvc:label></td>
<td><mvc:input path="username" cssErrorClass="formFieldError" /></td>
<td><mvc:errors path="username" /></td>
</tr>
<tr>
<td><mvc:label path="email">E-Mail</mvc:label></td>
<td><mvc:input path="email" cssErrorClass="formFieldError" /></td>
<td><mvc:errors path="email" /></td>
</tr>
<tr>
<td><mvc:label path="ccNumber">Credit Card Number</mvc:label></td>
<td><mvc:input path="ccNumber" cssErrorClass="formFieldError" /></td>
<td><mvc:errors path="ccNumber" /></td>
</tr>
<tr>
<td><mvc:label path="password">Password</mvc:label></td>
<td><mvc:password path="password" cssErrorClass="formFieldError" /></td>
<td><mvc:errors path="password" /></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
</mvc:form>
</body>
</html>
其中每一个样式的errors标签的path都被设置为模型类的属性名称,不设置则看不到相关输入字段的错误消息,想要在一个地方显示所有错误可将path值设置为*。例子中还定义了样式表类formFieldError,并将每个输入字段的cssErrorClass特性设置为该类,开头通过style标签将该类字段背景设置为黄色。
验证注解中使用message属性返回对应的错误消息,消息所在位置由ReloadableResourceBundleMessageSource设定,此处为src/main/resource文件夹下的message.property文件。
Pattern.user.password=Password should meet the requirements.
Size.user.username=Size must be between 3 and 20.
CreditCardNumber.user.ccNumber=invalid credit card number.
不难发现message.property文件中定义错误消息的格式为 注解名.实例名.属性名 = 错误消息内容
在浏览器中输入http://localhost:8080/BeanValidator/result.mvc,直接提交空表单可以获得如下结果
可看到未通过验证的输入栏背景变成黄色,旁边出现错误信息