|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
JSP and JSTL
Spring框架内置了使用Spring MVC与JSP和JSTL集成的功能。
视图解析器
在使用 JSP 开发时,您通常会声明一个 InternalResourceViewResolver bean。
InternalResourceViewResolver 可用于将请求分派到任何 Servlet 资源,尤其是 JSP 文件。作为最佳实践,我们强烈建议将您的 JSP 文件放在 'WEB-INF' 目录下的一个目录中,这样客户端就无法直接访问。
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
Spring的JSP标签库
Spring 提供了将请求参数绑定到命令对象的功能,如前几章所述。为了便于结合这些数据绑定功能开发 JSP 页面,Spring 提供了一些标签,使事情变得更加简单。所有 Spring 标签都具有 HTML 转义功能,可以启用或禁用字符转义。
The spring.tld 标签库描述符 (TLD) 已包含在 spring-webmvc.jar 中。
有关各个标签的综合参考,请浏览
API参考
或参阅标签库描述。
Spring的表单标签库
从2.0版本开始,Spring提供了一整套支持数据绑定的标签,用于处理使用JSP和Spring Web MVC时的表单元素。每个标签都支持其对应的HTML标签的一系列属性,使得这些标签易于使用且直观。生成的HTML符合HTML 4.01/XHTML 1.0标准。
与其它表单/输入标签库不同,Spring 的表单标签库是与 Spring Web MVC 集成的,这使得这些标签可以访问你的控制器处理的命令对象和参考数据。正如我们在下面的例子中所示,表单标签使得 JSP 更易于开发、阅读和维护。
我们逐一查看表单标签,并通过示例了解每个标签的使用方法。我们已经包含了某些标签需要进一步说明时生成的HTML片段。
配置
表单标签库包含在spring-webmvc.jar中。库描述符被称为spring-form.tld。
要在JSP页面顶部使用此库中的标签,请添加以下指令:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
其中 form 是您希望为此库中的标签使用的标签名称前缀。
表单标签
此标签渲染一个HTML 'form'元素,并为内部标签暴露绑定路径。它将命令对象放入PageContext中,以便内部标签可以访问命令对象。该库中的所有其他标签都是form标签的嵌套标签。
假设我们有一个名为User的领域对象。它是一个具有属性(如firstName和lastName)的JavaBean。我们可以将其用作表单控制器的表单支持对象,该控制器返回form.jsp。以下示例展示了form.jsp可能的样子:
<form:form>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
The firstName 和 lastName 值是从页面控制器放置在 PageContext 中的命令对象中检索到的。继续阅读以查看更复杂的示例,了解如何使用 form 标签与内部标签结合使用。
以下列表显示了生成的HTML,它看起来像一个标准表单:
<form method="POST">
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value="Harry"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value="Potter"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
上述JSP假设表单支持对象的变量名为
command。如果你将表单支持对象以其他名称放入模型中(这绝对是一个最佳实践),你可以将表单绑定到命名变量,如下例所示:
<form:form modelAttribute="user">
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
0 标签
此标签渲染一个HTML input 元素,并默认绑定值和 type='text'。
有关此标签的示例,请参见 表单标签。您还可以使用 HTML5 特定类型,例如 email、tel、date 等。
0 标签
此标签渲染一个HTML input 标签,并将 type 设置为 checkbox。
假设我们的User有偏好设置,例如新闻订阅和爱好列表。以下示例显示了Preferences类:
-
Java
-
Kotlin
public class Preferences {
private boolean receiveNewsletter;
private String[] interests;
private String favouriteWord;
public boolean isReceiveNewsletter() {
return receiveNewsletter;
}
public void setReceiveNewsletter(boolean receiveNewsletter) {
this.receiveNewsletter = receiveNewsletter;
}
public String[] getInterests() {
return interests;
}
public void setInterests(String[] interests) {
this.interests = interests;
}
public String getFavouriteWord() {
return favouriteWord;
}
public void setFavouriteWord(String favouriteWord) {
this.favouriteWord = favouriteWord;
}
}
class Preferences(
var receiveNewsletter: Boolean,
var interests: StringArray,
var favouriteWord: String
)
对应的 form.jsp 可以如下所示:
<form:form>
<table>
<tr>
<td>Subscribe to newsletter?:</td>
<%-- Approach 1: Property is of type java.lang.Boolean --%>
<td><form:checkbox path="preferences.receiveNewsletter"/></td>
</tr>
<tr>
<td>Interests:</td>
<%-- Approach 2: Property is of an array or of type java.util.Collection --%>
<td>
Quidditch: <form:checkbox path="preferences.interests" value="Quidditch"/>
Herbology: <form:checkbox path="preferences.interests" value="Herbology"/>
Defence Against the Dark Arts: <form:checkbox path="preferences.interests" value="Defence Against the Dark Arts"/>
</td>
</tr>
<tr>
<td>Favourite Word:</td>
<%-- Approach 3: Property is of type java.lang.Object --%>
<td>
Magic: <form:checkbox path="preferences.favouriteWord" value="Magic"/>
</td>
</tr>
</table>
</form:form>
有三种方法可以使用checkbox标签,应该能满足你所有的复选框需求。
-
方法一:当绑定值为类型
java.lang.Boolean时,如果绑定值为true,则将input(checkbox)标记为checked。value属性对应于setValue(Object)值属性的解析值。 -
方法二:当绑定的值为类型
array或java.util.Collection时,如果配置的setValue(Object)值存在于绑定的Collection中,则将input(checkbox)标记为checked。 -
方法三:对于任何其他绑定值类型,如果配置的
setValue(Object)等于绑定值,则将input(checkbox)标记为checked。
请注意,无论采用哪种方法,生成的 HTML 结构都是相同的。以下 HTML 代码片段定义了一些复选框:
<tr>
<td>Interests:</td>
<td>
Quidditch: <input name="preferences.interests" type="checkbox" value="Quidditch"/>
<input type="hidden" value="1" name="_preferences.interests"/>
Herbology: <input name="preferences.interests" type="checkbox" value="Herbology"/>
<input type="hidden" value="1" name="_preferences.interests"/>
Defence Against the Dark Arts: <input name="preferences.interests" type="checkbox" value="Defence Against the Dark Arts"/>
<input type="hidden" value="1" name="_preferences.interests"/>
</td>
</tr>
你可能没有预料到在每个复选框之后会看到额外的隐藏字段。
当HTML页面中的复选框未被选中时,其值不会作为HTTP请求参数的一部分发送到服务器,因此我们需要一个变通方法来解决HTML中的这个特殊问题,以便Spring表单数据绑定能够正常工作。checkbox 标签遵循现有的Spring约定,为每个复选框包含一个以下划线 (_) 开头的隐藏参数。通过这样做,实际上是告诉Spring“该复选框在表单提交时是可见的,我希望我的对象能够反映复选框的状态,无论它是否被选中。”
0 标签
此标签渲染多个HTML input 标签,并将 type 设置为 checkbox。
本节基于前一个checkbox标签部分的示例。有时,你可能不希望在JSP页面中列出所有可能的兴趣爱好。你更愿意在运行时提供可用选项的列表,并将其传递给标签。这就是checkboxes标签的目的。你可以传递一个Array、一个List或一个包含可用选项的Map到items属性中。通常,绑定属性是一个集合,以便它可以保存用户选择的多个值。以下示例展示了一个使用此标签的JSP:
<form:form>
<table>
<tr>
<td>Interests:</td>
<td>
<%-- Property is of an array or of type java.util.Collection --%>
<form:checkboxes path="preferences.interests" items="${interestList}"/>
</td>
</tr>
</table>
</form:form>
此示例假设 interestList 是一个 List 作为模型属性可用,其中包含要从中选择的值的字符串。如果你使用 Map,
映射条目的键用作值,而映射条目的值用作显示的标签。你还可以使用自定义对象,其中你可以通过使用 itemValue 提供值的属性名称,并通过使用 itemLabel 提供标签的属性名称。
0 标签
此标签渲染一个HTML input 元素,并将 type 设置为 radio。
一个典型的使用模式涉及多个标签实例绑定到同一个属性,但具有不同的值,如下例所示:
<tr>
<td>Sex:</td>
<td>
Male: <form:radiobutton path="sex" value="M"/> <br/>
Female: <form:radiobutton path="sex" value="F"/>
</td>
</tr>
0 标签
此标签渲染多个HTML input 元素,并将 type 设置为 radio。
与 checkboxes 标签 类似,你可能希望将可用选项作为运行时变量传递。对于这种用法,你可以使用 radiobuttons 标签。你可以传递一个 Array、一个 List 或一个包含可用选项的 Map。这些选项存储在 items 属性中。如果你使用 Map,则映射项的键用作值,而映射项的值用作显示的标签。你还可以使用自定义对象,并通过使用 itemValue 提供值属性名称,使用 itemLabel 提供标签属性名称,如下例所示:
<tr>
<td>Sex:</td>
<td><form:radiobuttons path="sex" items="${sexOptions}"/></td>
</tr>
0 标签
此标签渲染一个HTML input 标签,并将类型设置为 password,绑定值。
<tr>
<td>Password:</td>
<td>
<form:password path="password"/>
</td>
</tr>
请注意,默认情况下不会显示密码值。如果您确实希望显示密码值,可以将showPassword属性的值设置为true,如下例所示:
<tr>
<td>Password:</td>
<td>
<form:password path="password" value="^76525bvHGq" showPassword="true"/>
</td>
</tr>
0 标签
此标签渲染一个HTML 'select' 元素。它支持数据绑定到选中的选项以及使用嵌套的 option 和 options 标签。
假设一个User拥有一系列技能。对应的HTML可以如下所示:
<tr>
<td>Skills:</td>
<td><form:select path="skills" items="${skills}"/></td>
</tr>
如果User’s技能在草药学中,那么“技能”行的HTML源代码可能如下:
<tr>
<td>Skills:</td>
<td>
<select name="skills" multiple="true">
<option value="Potions">Potions</option>
<option value="Herbology" selected="selected">Herbology</option>
<option value="Quidditch">Quidditch</option>
</select>
</td>
</tr>
0 标签
此标签渲染一个HTML option 元素。它根据绑定的值设置 selected。以下HTML显示了它的典型输出:
<tr>
<td>House:</td>
<td>
<form:select path="house">
<form:option value="Gryffindor"/>
<form:option value="Hufflepuff"/>
<form:option value="Ravenclaw"/>
<form:option value="Slytherin"/>
</form:select>
</td>
</tr>
如果User’s号房子在格兰芬多,那么“House”行的HTML源代码如下:
<tr>
<td>House:</td>
<td>
<select name="house">
<option value="Gryffindor" selected="selected">Gryffindor</option> (1)
<option value="Hufflepuff">Hufflepuff</option>
<option value="Ravenclaw">Ravenclaw</option>
<option value="Slytherin">Slytherin</option>
</select>
</td>
</tr>
| 1 | 请注意添加了一个selected属性。 |
0 标签
此标签渲染一组HTML option 元素。它根据绑定值设置 selected 属性。
以下HTML显示了它的典型输出:
<tr>
<td>Country:</td>
<td>
<form:select path="country">
<form:option value="-" label="--Please Select"/>
<form:options items="${countryList}" itemValue="code" itemLabel="name"/>
</form:select>
</td>
</tr>
如果User居住在英国,“Country”行的HTML源代码如下:
<tr>
<td>Country:</td>
<td>
<select name="country">
<option value="-">--Please Select</option>
<option value="AT">Austria</option>
<option value="UK" selected="selected">United Kingdom</option> (1)
<option value="US">United States</option>
</select>
</td>
</tr>
| 1 | 请注意添加了一个selected属性。 |
如上例所示,option 标签与 options 标签的组合使用生成相同的标准 HTML,但允许你在 JSP 中显式指定一个仅用于显示的值(它应该位于此处),例如示例中的默认字符串:“-- 请选择”。
The items 属性通常填充一个项目对象的集合或数组。
itemValue 和 itemLabel 指向这些项目对象的 bean 属性,如果指定了的话。否则,项目对象本身将被转换为字符串。或者,您可以指定一个 Map 项,这种情况下,映射键被视为选项值,而映射值对应于选项标签。如果指定了 itemValue 或 itemLabel(或两者都指定了),则项目值属性适用于映射键,项目标签属性适用于映射值。
0 标签
此标签渲染一个HTML textarea 元素。以下HTML显示了它的典型输出:
<tr>
<td>Notes:</td>
<td><form:textarea path="notes" rows="3" cols="20"/></td>
<td><form:errors path="notes"/></td>
</tr>
0 标签
此标签渲染一个HTML input 标签,并将 type 设置为 hidden 的绑定值。要提交未绑定的隐藏值,请使用 HTML input 标签并将 type 设置为 hidden。
以下HTML显示了它的典型输出:
<form:hidden path="house"/>
如果我们选择将house值作为隐藏值提交,HTML代码如下:
<input name="house" type="hidden" value="Gryffindor"/>
0 标签
此标签在HTML span 元素中渲染字段错误。它提供了访问在控制器中创建的错误或与控制器关联的任何验证器创建的错误的途径。
假设我们希望在提交表单后显示firstName和lastName字段的所有错误消息。我们有一个针对User类的验证器,名为UserValidator,如下例所示:
-
Java
-
Kotlin
public class UserValidator implements Validator {
public boolean supports(Class candidate) {
return User.class.isAssignableFrom(candidate);
}
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.");
}
}
class UserValidator : Validator {
override fun supports(candidate: Class<*>): Boolean {
return User::class.java.isAssignableFrom(candidate)
}
override fun validate(obj: Any, errors: Errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.")
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.")
}
}
The form.jsp 可以如下所示:
<form:form>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
<%-- Show errors for firstName field --%>
<td><form:errors path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
<%-- Show errors for lastName field --%>
<td><form:errors path="lastName"/></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
如果我们提交一个表单,其中 firstName 和 lastName 字段的值为空,
则HTML代码如下:
<form method="POST">
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value=""/></td>
<%-- Associated errors to firstName field displayed --%>
<td><span name="firstName.errors">Field is required.</span></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value=""/></td>
<%-- Associated errors to lastName field displayed --%>
<td><span name="lastName.errors">Field is required.</span></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
如果我们想显示给定页面的整个错误列表怎么办?下一个示例
显示了errors标签还支持一些基本的通配符功能。
-
path="*": 显示所有错误。 -
path="lastName": 显示与lastName字段相关的所有错误。 -
如果省略
path,则仅显示对象错误。
以下示例在页面顶部显示一个错误列表,然后在字段旁边显示特定于字段的错误:
<form:form>
<form:errors path="*" cssClass="errorBox"/>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
<td><form:errors path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
<td><form:errors path="lastName"/></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
HTML代码如下:
<form method="POST">
<span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span>
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value=""/></td>
<td><span name="firstName.errors">Field is required.</span></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value=""/></td>
<td><span name="lastName.errors">Field is required.</span></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
The spring-form.tld 标签库描述符 (TLD) 已包含在 spring-webmvc.jar 中。
有关各个标签的综合参考,请浏览
API参考
或参阅标签库描述。
HTTP方法转换
REST 的一个关键原则是使用“统一接口”。这意味着所有资源(URL)都可以通过相同的四种 HTTP 方法进行操作:GET、PUT、POST 和 DELETE。对于每种方法,HTTP 规范定义了确切的语义。例如,GET 应该始终是一个安全的操作,意味着它没有副作用,而 PUT 或 DELETE 应该是幂等的,意味着你可以反复执行这些操作,但最终结果应该相同。虽然 HTTP 定义了这四种方法,但 HTML 只支持两种:GET 和 POST。幸运的是,有两种可能的变通方法:你可以使用 JavaScript 来执行 PUT 或 DELETE,或者在 HTML 表单中将“真实”的方法作为附加参数(以 HTML 表单中的隐藏输入字段的形式)来模拟 PUT 或 DELETE。Spring 的 HiddenHttpMethodFilter 使用了后一种技巧。这个过滤器是一个普通的 Servlet 过滤器,因此可以与任何 Web 框架结合使用(不仅仅是 Spring MVC)。将此过滤器添加到你的 web.xml 中,POST 请求会根据隐藏的 method 参数被转换为相应的 HTTP 方法请求。
为了支持HTTP方法转换,Spring MVC表单标签已更新以支持设置HTTP方法。例如,以下代码片段来自宠物诊所示例:
<form:form method="delete">
<p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>
前一个示例执行了一个HTTP POST,其中“真实”的DELETE方法隐藏在一个请求参数后面。它被HiddenHttpMethodFilter捕获,该值在web.xml中定义,如下例所示:
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<servlet-name>petclinic</servlet-name>
</filter-mapping>
以下示例显示了对应的@Controller方法:
-
Java
-
Kotlin
@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
this.clinic.deletePet(petId);
return "redirect:/owners/" + ownerId;
}
@RequestMapping(method = [RequestMethod.DELETE])
fun deletePet(@PathVariable ownerId: Int, @PathVariable petId: Int): String {
clinic.deletePet(petId)
return "redirect:/owners/$ownerId"
}