此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
JSP 和 JSTL
Spring Framework 具有用于将 Spring MVC 与 JSP 和 JSTL 一起使用的内置集成。
View 解析程序
使用 JSP 进行开发时,通常会声明InternalResourceViewResolver
豆。
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 页面的开发 data binding 功能,Spring 提供了一些标签,使事情变得更加容易。都 Spring 标签具有 HTML 转义功能,用于启用或禁用字符转义。
这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 更易于开发、阅读和维护。
我们将浏览 form 标记,并查看如何使用每个标记的示例。我们有 包含生成的 HTML 片段,其中某些标记需要进一步注释。
配置
表单标记库捆绑在spring-webmvc.jar
.库描述符为
叫spring-form.tld
.
要使用此库中的标记,请将以下指令添加到 JSP 的顶部 页:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
哪里form
是要用于此库中的标签的标签名称前缀。
表单标签
此标签呈现一个 HTML“form”元素,并公开一个指向内部标签的绑定路径
捆绑。它将命令对象放在PageContext
以便 Command 对象可以
通过 inner 标签访问。此库中的所有其他标签都是form
标记。
假设我们有一个名为User
.它是一个具有属性
如firstName
和lastName
.我们可以将其用作
form 控制器,该控制器返回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>
这firstName
和lastName
值是从放置在
这PageContext
通过页面控制器。继续阅读以查看更复杂的示例
如何将 inner 标记与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-backing 对象以其他名称放入模型中
(绝对是最佳实践),您可以将表单绑定到命名变量,因为
以下示例显示:
<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>
这input
标记
此标签呈现 HTMLinput
元素,其绑定值和type='text'
默认情况下。
有关此标记的示例,请参阅 Form 标记。您还可以使用
HTML5 特定的类型,例如email
,tel
,date
等。
这checkbox
标记
此标签呈现 HTMLinput
标签与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
这input(checkbox)
标记为checked
如果 bound 值为true
.这value
属性对应于setValue(Object)
value 属性。 -
方法二:当绑定值为
array
或java.util.Collection
这input(checkbox)
标记为checked
如果配置的setValue(Object)
value 为 存在于 Bound 中Collection
. -
方法三:对于任何其他绑定值类型,
input(checkbox)
标记为checked
如果配置的setValue(Object)
等于 bound 值。
请注意,无论采用哪种方法,都会生成相同的 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 页面中的复选框,则其值不会发送到
server 作为 HTTP 请求参数的一部分,因此我们需要一个
HTML 中此怪癖的解决方法,以便 Spring 表单数据绑定正常工作。这checkbox
标签遵循现有的 Spring 约定,即包含一个隐藏的参数
为每个复选框添加下划线 () 前缀。通过这样做,您可以有效地
告诉 Spring “复选框在表单中可见,我希望我的对象
无论如何,表单数据都会绑定以反映复选框的状态。_
这checkboxes
标记
此标签呈现多个 HTMLinput
标签中带有type
设置为checkbox
.
本节基于前面的示例。checkbox
tag 部分。有时,您更喜欢
不必在 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
可用作 Model 属性
,其中包含要从中选择的值的字符串。如果您使用Map
,
映射条目键用作值,映射条目的值用作
要显示的标签。您还可以使用自定义对象,您可以在其中提供
value 的属性名称itemValue
和标签itemLabel
.
这radiobutton
标记
此标签呈现 HTMLinput
元素替换为type
设置为radio
.
典型的使用模式涉及绑定到同一属性的多个标记实例 但具有不同的值,如下例所示:
<tr>
<td>Sex:</td>
<td>
Male: <form:radiobutton path="sex" value="M"/> <br/>
Female: <form:radiobutton path="sex" value="F"/>
</td>
</tr>
这radiobuttons
标记
此标签呈现多个 HTMLinput
元素替换为type
设置为radio
.
与checkboxes
标记,您可能希望
将 available options 作为运行时变量传入。对于此用法,您可以使用radiobuttons
标记。您传入一个Array
一个List
或Map
,其中包含
的items
财产。如果您使用Map
,则映射入口键为
used as the value 和 map entry 的值用作要显示的标签。
您还可以使用自定义对象,您可以在其中为值提供属性名称
通过使用itemValue
和标签itemLabel
,如下例所示:
<tr>
<td>Sex:</td>
<td><form:radiobuttons path="sex" items="${sexOptions}"/></td>
</tr>
这password
标记
此标签呈现 HTMLinput
标记中,并将类型设置为password
替换为绑定值。
<tr>
<td>Password:</td>
<td>
<form:password path="password"/>
</td>
</tr>
请注意,默认情况下,不会显示 password 值。如果您确实希望使用
password 值,则可以设置showPassword
属性设置为true
,如下例所示:
<tr>
<td>Password:</td>
<td>
<form:password path="password" value="^76525bvHGq" showPassword="true"/>
</td>
</tr>
这select
标记
此标签呈现 HTML 'select' 元素。它支持将数据绑定到选定的
选项以及使用嵌套的option
和options
标签。
假设User
有一个技能列表。相应的 HTML 可能如下所示:
<tr>
<td>Skills:</td>
<td><form:select path="skills" items="${skills}"/></td>
</tr>
如果User’s
skill 在 Herbology 中,则 'Skills' 行的 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>
这option
标记
此标签呈现 HTMLoption
元素。它设置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 位于格兰芬多中,则 '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 属性。 |
这options
标记
此标签呈现 HTMLoption
元素。它将selected
属性
基于 Bound 值。以下 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,例如
示例:“-- 请选择”。
这items
属性通常填充 item 对象的集合或数组。itemValue
和itemLabel
引用这些 Item 对象的 bean 属性,如果
指定。否则,item 对象本身将转换为字符串。或者
您可以指定Map
of items,在这种情况下,映射键被解释为 option
values 和 map 值对应于选项标签。如果itemValue
或itemLabel
(或两者兼而有之)
恰好也被指定,则 item 值属性适用于 map 键,并且
item label 属性适用于 map 值。
这textarea
标记
此标签呈现 HTMLtextarea
元素。以下 HTML 显示了它的典型输出:
<tr>
<td>Notes:</td>
<td><form:textarea path="notes" rows="3" cols="20"/></td>
<td><form:errors path="notes"/></td>
</tr>
这hidden
标记
此标签呈现 HTMLinput
标签与type
设置为hidden
替换为绑定值。提交
未绑定的隐藏值,请使用 HTMLinput
标签与type
设置为hidden
.
以下 HTML 显示了它的典型输出:
<form:hidden path="house"/>
如果我们选择提交house
值设置为隐藏值,则 HTML 将如下所示:
<input name="house" type="hidden" value="Gryffindor"/>
这errors
标记
此标签在 HTML 中呈现字段错误span
元素。它提供对错误的访问
在您的控制器中创建的,或者是由与
您的控制器。
假设我们想要显示firstName
和lastName
字段。我们有一个 Validator 用于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.")
}
}
这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>
这spring-form.tld
标记库描述符 (TLD) 包含在spring-webmvc.jar
.
有关各个标记的全面参考,请浏览 API 参考或查看标记库描述。
HTTP 方法转换
REST 的一个关键原则是使用“统一接口”。这意味着所有
可以使用相同的四种 HTTP 方法作资源 (URL):GET、PUT、POST、
和 DELETE 的 DELETE 命令。对于每种方法,HTTP 规范定义了确切的语义。为
例如,GET 应该始终是一个安全的作,这意味着它没有副作用,
PUT 或 DELETE 应该是幂等的,这意味着您可以重复这些作
一遍又一遍,但最终结果应该是相同的。虽然 HTTP 定义了这些
四种方法,HTML 只支持两种:GET 和 POST。幸运的是,有两种可能
解决方法:您可以使用 JavaScript 执行 PUT 或 DELETE,也可以执行 POST
使用 “real” 方法作为附加参数(建模为
HTML 表单)。Spring的HiddenHttpMethodFilter
使用后一个技巧。这
filter 是一个普通的 Servlet 过滤器,因此,它可以与任何
Web 框架(不仅仅是 Spring MVC)。将此过滤器添加到您的 web.xml,然后 POST
带有隐藏的method
parameter 转换为对应的 HTTP 方法
请求。
为了支持 HTTP 方法转换,更新了 Spring MVC 表单标记以支持设置 HTTP 方法。例如,以下代码片段来自 Pet Clinic 示例:
<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"
}