docs: 更新文档

This commit is contained in:
dunwu 2023-02-17 18:38:49 +08:00
parent 5f84793aa9
commit 511774b43e
4 changed files with 762 additions and 59 deletions

View File

@ -1,5 +1,5 @@
---
title: Spring MVC 之 Controller
title: Spring MVC 组件
categories:
- Java
- 框架
@ -15,7 +15,7 @@ date: 2023-02-14 19:21:22
permalink: /pages/5d002f/
---
# Spring MVC 之 Controller
# Spring MVC 组件
Spring MVC 提供了一种基于注解的编程模型,`@Controller` 和 `@RestController` 组件使用注解来表达请求映射、请求输入、异常处理等。注解控制器具有灵活的方法签名,并且不必扩展基类或实现特定接口。以下示例显示了一个由注解定义的控制器:

View File

@ -43,13 +43,7 @@ The rules for combining global and local configuration are generally additive
## `@CrossOrigin`
[See equivalent in the Reactive stack](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-cors-controller)
The [`@CrossOrigin`](https://docs.spring.io/spring-framework/docs/6.0.4/javadoc-api/org/springframework/web/bind/annotation/CrossOrigin.html) annotation enables cross-origin requests on annotated controller methods, as the following example shows:
Java
Kotlin
[`@CrossOrigin`](https://docs.spring.io/spring-framework/docs/6.0.4/javadoc-api/org/springframework/web/bind/annotation/CrossOrigin.html) 注解在带注解的 Controller 方法上启用跨源请求,如以下示例所示:
```java
@RestController
@ -69,21 +63,17 @@ public class AccountController {
}
```
By default, `@CrossOrigin` allows:
默认,`@CrossOrigin` 允许访问:
- All origins.
- All headers.
- All HTTP methods to which the controller method is mapped.
- 所以 origin
- 所以 header
- 所以 Controller 方法映射到的 HTTP 方法
`allowCredentials` is not enabled by default, since that establishes a trust level that exposes sensitive user-specific information (such as cookies and CSRF tokens) and should only be used where appropriate. When it is enabled either `allowOrigins` must be set to one or more specific domain (but not the special value `"*"`) or alternatively the `allowOriginPatterns` property may be used to match to a dynamic set of origins.
`allowCredentials` 默认情况下不启用,因为它建立了一个信任级别,可以公开敏感的用户特定信息(例如 cookie 和 CSRF 令牌),并且只应在适当的情况下使用。启用时,必须将 `allowOrigins` 设置为一个或多个特定域(但不是特殊值 `"*"`),或者 `allowOriginPatterns` 属性可用于匹配一组动态来源。
`maxAge` is set to 30 minutes.
`maxAge` 单位为分钟
`@CrossOrigin` is supported at the class level, too, and is inherited by all methods, as the following example shows:
Java
Kotlin
`@CrossOrigin` 也支持类级别,并且被所有方继承,如下所示:
```java
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@ -103,11 +93,7 @@ public class AccountController {
}
```
You can use `@CrossOrigin` at both the class level and the method level, as the following example shows:
Java
Kotlin
可以同时在类级别和方法级别上使用 `@CrossOrigin`
```java
@CrossOrigin(maxAge = 3600)
@ -128,31 +114,23 @@ public class AccountController {
}
```
## Global Configuration
## 全局配置
[See equivalent in the Reactive stack](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-cors-global)
除了细粒度的控制器方法级别配置之外,您可能还想定义一些全局 CORS 配置。您可以在任何 `HandlerMapping` 上单独设置基于 URL 的 `CorsConfiguration` 映射。但是,大多数应用程序使用 MVC Java 配置或 MVC XML 命名空间来执行此操作。
In addition to fine-grained, controller method level configuration, you probably want to define some global CORS configuration, too. You can set URL-based `CorsConfiguration` mappings individually on any `HandlerMapping`. Most applications, however, use the MVC Java configuration or the MVC XML namespace to do that.
默认情况下,全局配置启用以下功能:
By default, global configuration enables the following:
- 所以 origin
- 所以 header
- `GET`、`HEAD` 和 `POST` 方法
- All origins.
- All headers.
- `GET`, `HEAD`, and `POST` methods.
`allowCredentials` 默认情况下不启用,因为它建立了一个信任级别,可以公开敏感的用户特定信息(例如 cookie 和 CSRF 令牌),并且只应在适当的情况下使用。启用时,必须将 `allowOrigins` 设置为一个或多个特定域(但不是特殊值 `"*"`),或者 `allowOriginPatterns` 属性可用于匹配一组动态来源。
`allowCredentials` is not enabled by default, since that establishes a trust level that exposes sensitive user-specific information (such as cookies and CSRF tokens) and should only be used where appropriate. When it is enabled either `allowOrigins` must be set to one or more specific domain (but not the special value `"*"`) or alternatively the `allowOriginPatterns` property may be used to match to a dynamic set of origins.
`maxAge` 单位为分钟
`maxAge` is set to 30 minutes.
### Java 配置
### Java Configuration
[See equivalent in the Reactive stack](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-cors-global)
To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback, as the following example shows:
Java
Kotlin
要在 MVC Java 配置中启用 CORS您可以使用 `CorsRegistry` 回调,如以下示例所示:
```java
@Configuration
@ -174,9 +152,9 @@ public class WebConfig implements WebMvcConfigurer {
}
```
### XML Configuration
### XML 配置
To enable CORS in the XML namespace, you can use the `<mvc:cors>` element, as the following example shows:
要在 XML 命名空间中启用 CORS可以使用 `<mvc:cors>` 元素,如以下示例所示:
```xml
<mvc:cors>
@ -194,21 +172,11 @@ To enable CORS in the XML namespace, you can use the `<mvc:cors>` element, as th
</mvc:cors>
```
## CORS Filter
## CORS 过滤器
[See equivalent in the Reactive stack](https://docs.spring.io/spring-framework/docs/current/reference/html/webflux-cors.html#webflux-cors-webfilter)
可以通过 Spring 内置的 [`CorsFilter`](https://docs.spring.io/spring-framework/docs/6.0.4/javadoc-api/org/springframework/web/filter/CorsFilter.html) 支持 CORS。
You can apply CORS support through the built-in [`CorsFilter`](https://docs.spring.io/spring-framework/docs/6.0.4/javadoc-api/org/springframework/web/filter/CorsFilter.html).
| | If you try to use the `CorsFilter` with Spring Security, keep in mind that Spring Security has [built-in support](https://docs.spring.io/spring-security/reference/servlet/integrations/cors.html) for CORS. |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| | |
To configure the filter, pass a `CorsConfigurationSource` to its constructor, as the following example shows:
Java
Kotlin
要配置过滤器,请将 `CorsConfigurationSource` 传递给它的构造函数,如以下示例所示:
```java
CorsConfiguration config = new CorsConfiguration();

View File

@ -0,0 +1,735 @@
---
title: Spring MVC 之视图技术
categories:
- Java
- 框架
- Spring
- SpringWeb
tags:
- Java
- 框架
- Spring
- Web
- View
---
# Spring MVC 之视图技术
Spring MVC 中视图技术的使用是可插拔的。无论决定使用 Thymeleaf、Groovy 等模板引擎、JSP 还是其他技术,都可以通过配置来更改。
Spring MVC 的视图位于该应用程序的内部信任边界内。 视图可以访问应用程序上下文的所有 bean。 因此,不建议在模板可由外部源编辑的应用程序中使用 Spring MVC 的模板支持,因为这可能会产生安全隐患。
## Thymeleaf
[Thymeleaf](https://www.thymeleaf.org/) 是一个现代服务器端 Java 模板引擎,它强调自然的 HTML 模板,可以通过双击在浏览器中预览,而无需运行服务器,这对于 UI 模板的独立工作(例如,由设计师)非常有帮助。
Thymeleaf 与 Spring MVC 的集成由 Thymeleaf 项目管理。 配置涉及一些 bean 声明,例如 `ServletContextTemplateResolver`、`SpringTemplateEngine` 和 `ThymeleafViewResolver`。 有关详细信息,请参阅 [Thymeleaf+Spring](https://www.thymeleaf.org/documentation.html)。
## FreeMarker
[Apache FreeMarker](https://freemarker.apache.org/) 是一个模板引擎,用于生成从 HTML 到电子邮件等任何类型的文本内容。 Spring 框架内置了 Spring MVC 与 FreeMarker 模板结合使用的集成。
### 视图配置
以下示例显示了如何将 FreeMarker 配置为视图技术:
```java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
}
// Configure FreeMarker...
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
return configurer;
}
}
```
以下示例显示了如何在 XML 中配置相同的内容:
```xml
<mvc:annotation-driven/>
<mvc:view-resolvers>
<mvc:freemarker/>
</mvc:view-resolvers>
<!-- Configure FreeMarker... -->
<mvc:freemarker-configurer>
<mvc:template-loader-path location="/WEB-INF/freemarker"/>
</mvc:freemarker-configurer>
```
或者,您也可以声明 `FreeMarkerConfigurer` 以完全控制所有属性,如以下示例所示:
```xml
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>
```
您的模板需要存储在前面示例中所示的 `FreeMarkerConfigurer` 指定的目录中。鉴于前面的配置,如果您的控制器返回视图名称 `welcome`,解析器将查找 `/WEB-INF/freemarker/welcome.ftl` 模板。
### FreeMarker Configuration
可以通过在 `FreeMarkerConfigurer` 上设置适当的 bean 属性,将 FreeMarker 'Settings' 和 'SharedVariables' 直接传递给 FreeMarker `Configuration` 对象(由 Spring 管理)。 `freemarkerSettings` 属性需要一个 `java.util.Properties` 对象,`freemarkerVariables` 属性需要一个 `java.util.Map`。 以下示例显示了如何使用 `FreeMarkerConfigurer`
```xml
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape"/>
</map>
</property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
```
有关应用于 `Configuration` 对象的设置和变量的详细信息,请参阅 FreeMarker 文档。
### 表单处理
Spring 提供了一个用于 JSP 的标记库,其中包含一个 `<spring:bind/>` 元素。 此元素主要让表单显示来自表单支持对象的值,并显示来自 Web 或业务层中的“验证器”的验证失败的结果。 Spring 还支持 FreeMarker 中的相同功能,以及用于生成表单输入元素的额外便利宏。
#### The Bind Macros
A standard set of macros are maintained within the `spring-webmvc.jar` file for FreeMarker, so they are always available to a suitably configured application.
Some of the macros defined in the Spring templating libraries are considered internal (private), but no such scoping exists in the macro definitions, making all macros visible to calling code and user templates. The following sections concentrate only on the macros you need to directly call from within your templates. If you wish to view the macro code directly, the file is called `spring.ftl` and is in the `org.springframework.web.servlet.view.freemarker` package.
#### Simple Binding
In your HTML forms based on FreeMarker templates that act as a form view for a Spring MVC controller, you can use code similar to the next example to bind to field values and display error messages for each input field in similar fashion to the JSP equivalent. The following example shows a `personForm` view:
```xml
<!-- FreeMarker macros have to be imported into a namespace.
We strongly recommend sticking to 'spring'. -->
<#import "/spring.ftl" as spring/>
<html>
...
<form action="" method="POST">
Name:
<@spring.bind "personForm.name"/>
<input type="text"
name="${spring.status.expression}"
value="${spring.status.value?html}"/><br />
<#list spring.status.errorMessages as error> <b>${error}</b> <br /> </#list>
<br />
...
<input type="submit" value="submit"/>
</form>
...
</html>
```
`<@spring.bind>` requires a 'path' argument, which consists of the name of your command object (it is 'command', unless you changed it in your controller configuration) followed by a period and the name of the field on the command object to which you wish to bind. You can also use nested fields, such as `command.address.street`. The `bind` macro assumes the default HTML escaping behavior specified by the `ServletContext` parameter `defaultHtmlEscape` in `web.xml`.
An alternative form of the macro called `<@spring.bindEscaped>` takes a second argument that explicitly specifies whether HTML escaping should be used in the status error messages or values. You can set it to `true` or `false` as required. Additional form handling macros simplify the use of HTML escaping, and you should use these macros wherever possible. They are explained in the next section.
#### Input Macros
Additional convenience macros for FreeMarker simplify both binding and form generation (including validation error display). It is never necessary to use these macros to generate form input fields, and you can mix and match them with simple HTML or direct calls to the Spring bind macros that we highlighted previously.
The following table of available macros shows the FreeMarker Template (FTL) definitions and the parameter list that each takes:
| macro | FTL definition |
| :------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- |
| `message` (output a string from a resource bundle based on the code parameter) | <@spring.message code/> |
| `messageText` (output a string from a resource bundle based on the code parameter, falling back to the value of the default parameter) | <@spring.messageText code, text/> |
| `url` (prefix a relative URL with the applications context root) | <@spring.url relativeUrl/> |
| `formInput` (standard input field for gathering user input) | <@spring.formInput path, attributes, fieldType/> |
| `formHiddenInput` (hidden input field for submitting non-user input) | <@spring.formHiddenInput path, attributes/> |
| `formPasswordInput` (standard input field for gathering passwords. Note that no value is ever populated in fields of this type.) | <@spring.formPasswordInput path, attributes/> |
| `formTextarea` (large text field for gathering long, freeform text input) | <@spring.formTextarea path, attributes/> |
| `formSingleSelect` (drop down box of options that let a single required value be selected) | <@spring.formSingleSelect path, options, attributes/> |
| `formMultiSelect` (a list box of options that let the user select 0 or more values) | <@spring.formMultiSelect path, options, attributes/> |
| `formRadioButtons` (a set of radio buttons that let a single selection be made from the available choices) | <@spring.formRadioButtons path, options separator, attributes/> |
| `formCheckboxes` (a set of checkboxes that let 0 or more values be selected) | <@spring.formCheckboxes path, options, separator, attributes/> |
| `formCheckbox` (a single checkbox) | <@spring.formCheckbox path, attributes/> |
| `showErrors` (simplify display of validation errors for the bound field) | <@spring.showErrors separator, classOrStyle/> |
| | In FreeMarker templates, `formHiddenInput` and `formPasswordInput` are not actually required, as you can use the normal `formInput` macro, specifying `hidden` or `password` as the value for the `fieldType` parameter. |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| | |
The parameters to any of the above macros have consistent meanings:
- `path`: The name of the field to bind to (for example, "command.name")
- `options`: A `Map` of all the available values that can be selected from in the input field. The keys to the map represent the values that are POSTed back from the form and bound to the command object. Map objects stored against the keys are the labels displayed on the form to the user and may be different from the corresponding values posted back by the form. Usually, such a map is supplied as reference data by the controller. You can use any `Map` implementation, depending on required behavior. For strictly sorted maps, you can use a `SortedMap` (such as a `TreeMap`) with a suitable `Comparator` and, for arbitrary Maps that should return values in insertion order, use a `LinkedHashMap` or a `LinkedMap` from `commons-collections`.
- `separator`: Where multiple options are available as discreet elements (radio buttons or checkboxes), the sequence of characters used to separate each one in the list (such as `<br>`).
- `attributes`: An additional string of arbitrary tags or text to be included within the HTML tag itself. This string is echoed literally by the macro. For example, in a `textarea` field, you may supply attributes (such as 'rows="5" cols="60"'), or you could pass style information such as 'style="border:1px solid silver"'.
- `classOrStyle`: For the `showErrors` macro, the name of the CSS class that the `span` element that wraps each error uses. If no information is supplied (or the value is empty), the errors are wrapped in `<b></b>` tags.
The following sections outline examples of the macros.
Input Fields
The `formInput` macro takes the `path` parameter (`command.name`) and an additional `attributes` parameter (which is empty in the upcoming example). The macro, along with all other form generation macros, performs an implicit Spring bind on the path parameter. The binding remains valid until a new bind occurs, so the `showErrors` macro does not need to pass the path parameter againit operates on the field for which a binding was last created.
The `showErrors` macro takes a separator parameter (the characters that are used to separate multiple errors on a given field) and also accepts a second parameterthis time, a class name or style attribute. Note that FreeMarker can specify default values for the attributes parameter. The following example shows how to use the `formInput` and `showErrors` macros:
```xml
<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>
```
The next example shows the output of the form fragment, generating the name field and displaying a validation error after the form was submitted with no value in the field. Validation occurs through Springs Validation framework.
The generated HTML resembles the following example:
```jsp
Name:
<input type="text" name="name" value="">
<br>
<b>required</b>
<br>
<br>
```
The `formTextarea` macro works the same way as the `formInput` macro and accepts the same parameter list. Commonly, the second parameter (`attributes`) is used to pass style information or `rows` and `cols` attributes for the `textarea`.
Selection Fields
You can use four selection field macros to generate common UI value selection inputs in your HTML forms:
- `formSingleSelect`
- `formMultiSelect`
- `formRadioButtons`
- `formCheckboxes`
Each of the four macros accepts a `Map` of options that contains the value for the form field and the label that corresponds to that value. The value and the label can be the same.
The next example is for radio buttons in FTL. The form-backing object specifies a default value of 'London' for this field, so no validation is necessary. When the form is rendered, the entire list of cities to choose from is supplied as reference data in the model under the name 'cityMap'. The following listing shows the example:
```jsp
...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>
```
The preceding listing renders a line of radio buttons, one for each value in `cityMap`, and uses a separator of `""`. No additional attributes are supplied (the last parameter to the macro is missing). The `cityMap` uses the same `String` for each key-value pair in the map. The maps keys are what the form actually submits as `POST` request parameters. The map values are the labels that the user sees. In the preceding example, given a list of three well known cities and a default value in the form backing object, the HTML resembles the following:
```jsp
Town:
<input type="radio" name="address.town" value="London">London</input>
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input>
<input type="radio" name="address.town" value="New York">New York</input>
```
If your application expects to handle cities by internal codes (for example), you can create the map of codes with suitable keys, as the following example shows:
Java
Kotlin
```java
protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
Map<String, String> cityMap = new LinkedHashMap<>();
cityMap.put("LDN", "London");
cityMap.put("PRS", "Paris");
cityMap.put("NYC", "New York");
Map<String, Object> model = new HashMap<>();
model.put("cityMap", cityMap);
return model;
}
```
The code now produces output where the radio values are the relevant codes, but the user still sees the more user-friendly city names, as follows:
```jsp
Town:
<input type="radio" name="address.town" value="LDN">London</input>
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input>
<input type="radio" name="address.town" value="NYC">New York</input>
```
#### HTML Escaping
Default usage of the form macros described earlier results in HTML elements that are HTML 4.01 compliant and that use the default value for HTML escaping defined in your `web.xml` file, as used by Springs bind support. To make the elements be XHTML compliant or to override the default HTML escaping value, you can specify two variables in your template (or in your model, where they are visible to your templates). The advantage of specifying them in the templates is that they can be changed to different values later in the template processing to provide different behavior for different fields in your form.
To switch to XHTML compliance for your tags, specify a value of `true` for a model or context variable named `xhtmlCompliant`, as the following example shows:
```jsp
<#-- for FreeMarker -->
<#assign xhtmlCompliant = true>
```
After processing this directive, any elements generated by the Spring macros are now XHTML compliant.
In similar fashion, you can specify HTML escaping per field, as the following example shows:
```jsp
<#-- until this point, default HTML escaping is used -->
<#assign htmlEscape = true>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name"/>
<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML escaping off -->
```
## Groovy
The [Groovy Markup Template Engine](https://groovy-lang.org/templating.html#_the_markuptemplateengine) is primarily aimed at generating XML-like markup (XML, XHTML, HTML5, and others), but you can use it to generate any text-based content. The Spring Framework has a built-in integration for using Spring MVC with Groovy Markup.
[Groovy 标记模板引擎](https://groovy-lang.org/templating.html#_the_markuptemplateengine) 主要用于生成类似 XML 的标记XML、XHTML、HTML5 等),但可以使用它来生成任何基于文本的内容。 Spring Framework 具有将 Spring MVC 与 Groovy 标记结合使用的内置集成。
### 配置
以下示例显示如何配置 Groovy 标记模板引擎:
```java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.groovy();
}
// Configure the Groovy Markup Template Engine...
@Bean
public GroovyMarkupConfigurer groovyMarkupConfigurer() {
GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
configurer.setResourceLoaderPath("/WEB-INF/");
return configurer;
}
}
```
以下示例显示了如何在 XML 中配置相同的内容:
```xml
<mvc:annotation-driven/>
<mvc:view-resolvers>
<mvc:groovy/>
</mvc:view-resolvers>
<!-- Configure the Groovy Markup Template Engine... -->
<mvc:groovy-configurer resource-loader-path="/WEB-INF/"/>
```
### 示例
与传统的模板引擎不同Groovy 标记依赖于使用构建器语法的 DSL。以下示例显示了 HTML 页面的示例模板:
```groovy
yieldUnescaped '<!DOCTYPE html>'
html(lang:'en') {
head {
meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"')
title('My page')
}
body {
p('This is an example of HTML contents')
}
}
```
## 脚本视图
Spring 有一个内置的集成,可以将 Spring MVC 与任何可以在 [JSR-223](https://www.jcp.org/en/jsr/detail?id=223) 之上运行的模板库一起使用 Java 脚本引擎。 我们在不同的脚本引擎上测试了以下模板库:
| 脚本库 | 脚本引擎 |
| :--------------------------------------------------------------------------------- | :---------------------------------------------------- |
| [Handlebars](https://handlebarsjs.com/) | [Nashorn](https://openjdk.java.net/projects/nashorn/) |
| [Mustache](https://mustache.github.io/) | [Nashorn](https://openjdk.java.net/projects/nashorn/) |
| [React](https://facebook.github.io/react/) | [Nashorn](https://openjdk.java.net/projects/nashorn/) |
| [EJS](https://www.embeddedjs.com/) | [Nashorn](https://openjdk.java.net/projects/nashorn/) |
| [ERB](https://www.stuartellis.name/articles/erb/) | [JRuby](https://www.jruby.org/) |
| [String templates](https://docs.python.org/2/library/string.html#template-strings) | [Jython](https://www.jython.org/) |
| [Kotlin Script templating](https://github.com/sdeleuze/kotlin-script-templating) | [Kotlin](https://kotlinlang.org/) |
|
### 要求
需要在类路径中包含脚本引擎,具体细节因脚本引擎而异:
- The [Nashorn](https://openjdk.java.net/projects/nashorn/) Java 8+ 提供了 JavaScript 引擎。强烈建议使用可用的最新更新版本。
- [JRuby](https://www.jruby.org/) 应该作为 Ruby 支持的依赖项添加。
- [Jython](https://www.jython.org/) 应该作为 Python 支持的依赖项添加。
- `org.jetbrains.kotlin:kotlin-script-util` 依赖项和包含 `org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory` 行的 `META-INF/services/javax.script.ScriptEngineFactory` 文件应该被添加 Kotlin 脚本支持。 有关详细信息,请参阅[此示例](https://github.com/sdeleuze/kotlin-script-templating)。
您需要有脚本模板库。 为 JavaScript 做到这一点的一种方法是通过 [WebJars](https://www.webjars.org/)。
### 脚本模板
可以声明一个 `ScriptTemplateConfigurer` 来指定要使用的脚本引擎、要加载的脚本文件、调用什么函数来渲染模板等等。 以下示例使用 Mustache 模板和 Nashorn JavaScript 引擎:
```java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.scriptTemplate();
}
@Bean
public ScriptTemplateConfigurer configurer() {
ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
configurer.setEngineName("nashorn");
configurer.setScripts("mustache.js");
configurer.setRenderObject("Mustache");
configurer.setRenderFunction("render");
return configurer;
}
}
```
以下示例显示了 XML 中的相同配置:
```xml
<mvc:annotation-driven/>
<mvc:view-resolvers>
<mvc:script-template/>
</mvc:view-resolvers>
<mvc:script-template-configurer engine-name="nashorn" render-object="Mustache" render-function="render">
<mvc:script location="mustache.js"/>
</mvc:script-template-configurer>
```
对于 Java 和 XML 配置controller 看起来没有什么不同,如以下示例所示:
```java
@Controller
public class SampleController {
@GetMapping("/sample")
public String test(Model model) {
model.addAttribute("title", "Sample title");
model.addAttribute("body", "Sample body");
return "template";
}
}
```
以下示例显示了 Mustache 模板:
```html
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<p>{{body}}</p>
</body>
</html>
```
使用以下参数调用渲染函数:
- `String template`: 模板内容
- `地图模型`:视图模型
- `RenderingContext renderingContext` [`RenderingContext`](https://docs.spring.io/spring-framework/docs/6.0.5/javadoc-api/org/springframework/web/servlet/view/script/RenderingContext.html) 允许访问应用上下文、语言环境、模板加载器和 URL自 5.0 起)
如果您的模板技术需要一些自定义,您可以提供一个实现自定义渲染功能的脚本。 例如,[Handlerbars](https://handlebarsjs.com/) 需要在使用之前编译模板,并且需要一个 [polyfill](https://en.wikipedia.org/wiki/Polyfill) 来模拟一些浏览器工具,但在服务器端脚本引擎中不可用。
以下示例显示了如何执行此操作:
```java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.scriptTemplate();
}
@Bean
public ScriptTemplateConfigurer configurer() {
ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
configurer.setEngineName("nashorn");
configurer.setScripts("polyfill.js", "handlebars.js", "render.js");
configurer.setRenderFunction("render");
configurer.setSharedEngine(false);
return configurer;
}
}
```
`polyfill.js` 只定义了 Handlebars 正常运行所需的 `window` 对象,如下:
```javascript
var window = {}
```
这个基本的 `render.js` 实现在使用之前编译模板。 生产就绪的实现还应该存储任何重复使用的缓存模板或预编译模板。 您可以在脚本端这样做(并处理您需要的任何定制——管理模板引擎配置,例如)。 以下示例显示了如何执行此操作:
```javascript
function render(template, model) {
var compiledTemplate = Handlebars.compile(template)
return compiledTemplate(model)
}
```
查看 Spring Framework 单元测试,[Java](https://github.com/spring-projects/spring-framework/tree/main/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script) 和[资源](https://github.com/spring-projects/spring-framework/tree/main/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script),以获取更多配置示例。
## JSP 和 JSTL
Spring Framework 具有将 Spring MVC 与 JSP 和 JSTL 结合使用的内置集成。
> 更多内容详见:[Spring 官方文档之 JSP and JSTL](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-view-jsp)
## RSS and Atom
Both `AbstractAtomFeedView` and `AbstractRssFeedView` inherit from the `AbstractFeedView` base class and are used to provide Atom and RSS Feed views, respectively. They are based on [ROME](https://rometools.github.io/rome/) project and are located in the package `org.springframework.web.servlet.view.feed`.
`AbstractAtomFeedView` requires you to implement the `buildFeedEntries()` method and optionally override the `buildFeedMetadata()` method (the default implementation is empty). The following example shows how to do so:
`AbstractAtomFeedView``AbstractRssFeedView` 都继承自 `AbstractFeedView` 基类,分别用于提供 Atom 和 RSS Feed 视图。 它们基于 [ROME](https://rometools.github.io/rome/) 项目,位于 org.springframework.web.servlet.view.feed 包中。
`AbstractAtomFeedView` 要求您实现 `buildFeedEntries()` 方法并可选择覆盖 `buildFeedMetadata()` 方法(默认实现为空)。 以下示例显示了如何执行此操作:
```java
public class SampleContentAtomView extends AbstractAtomFeedView {
@Override
protected void buildFeedMetadata(Map<String, Object> model,
Feed feed, HttpServletRequest request) {
// implementation omitted
}
@Override
protected List<Entry> buildFeedEntries(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// implementation omitted
}
}
```
Similar requirements apply for implementing `AbstractRssFeedView`, as the following example shows:
```java
public class SampleContentRssView extends AbstractRssFeedView {
@Override
protected void buildFeedMetadata(Map<String, Object> model,
Channel feed, HttpServletRequest request) {
// implementation omitted
}
@Override
protected List<Item> buildFeedItems(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// implementation omitted
}
}
```
The `buildFeedItems()` and `buildFeedEntries()` methods pass in the HTTP request, in case you need to access the Locale. The HTTP response is passed in only for the setting of cookies or other HTTP headers. The feed is automatically written to the response object after the method returns.
For an example of creating an Atom view, see Alef Arendsens Spring Team Blog [entry](https://spring.io/blog/2009/03/16/adding-an-atom-view-to-an-application-using-spring-s-rest-support).
## PDF and Excel
Spring offers ways to return output other than HTML, including PDF and Excel spreadsheets. This section describes how to use those features.
### Introduction to Document Views
An HTML page is not always the best way for the user to view the model output, and Spring makes it simple to generate a PDF document or an Excel spreadsheet dynamically from the model data. The document is the view and is streamed from the server with the correct content type, to (hopefully) enable the client PC to run their spreadsheet or PDF viewer application in response.
In order to use Excel views, you need to add the Apache POI library to your classpath. For PDF generation, you need to add (preferably) the OpenPDF library.
| | You should use the latest versions of the underlying document-generation libraries, if possible. In particular, we strongly recommend OpenPDF (for example, OpenPDF 1.2.12) instead of the outdated original iText 2.1.7, since OpenPDF is actively maintained and fixes an important vulnerability for untrusted PDF content. |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| | |
### PDF Views
A simple PDF view for a word list could extend `org.springframework.web.servlet.view.document.AbstractPdfView` and implement the `buildPdfDocument()` method, as the following example shows:
Java
Kotlin
```java
public class PdfWordList extends AbstractPdfView {
protected void buildPdfDocument(Map<String, Object> model, Document doc, PdfWriter writer,
HttpServletRequest request, HttpServletResponse response) throws Exception {
List<String> words = (List<String>) model.get("wordList");
for (String word : words) {
doc.add(new Paragraph(word));
}
}
}
```
A controller can return such a view either from an external view definition (referencing it by name) or as a `View` instance from the handler method.
### Excel Views
Since Spring Framework 4.2, `org.springframework.web.servlet.view.document.AbstractXlsView` is provided as a base class for Excel views. It is based on Apache POI, with specialized subclasses (`AbstractXlsxView` and `AbstractXlsxStreamingView`) that supersede the outdated `AbstractExcelView` class.
The programming model is similar to `AbstractPdfView`, with `buildExcelDocument()` as the central template method and controllers being able to return such a view from an external definition (by name) or as a `View` instance from the handler method.
## Jackson
[See equivalent in the Reactive stack](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-view-httpmessagewriter)
Spring offers support for the Jackson JSON library.
### Jackson-based JSON MVC Views
[See equivalent in the Reactive stack](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-view-httpmessagewriter)
The `MappingJackson2JsonView` uses the Jackson librarys `ObjectMapper` to render the response content as JSON. By default, the entire contents of the model map (with the exception of framework-specific classes) are encoded as JSON. For cases where the contents of the map need to be filtered, you can specify a specific set of model attributes to encode by using the `modelKeys` property. You can also use the `extractValueFromSingleKeyModel` property to have the value in single-key models extracted and serialized directly rather than as a map of model attributes.
You can customize JSON mapping as needed by using Jacksons provided annotations. When you need further control, you can inject a custom `ObjectMapper` through the `ObjectMapper` property, for cases where you need to provide custom JSON serializers and deserializers for specific types.
### Jackson-based XML Views
[See equivalent in the Reactive stack](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-view-httpmessagewriter)
`MappingJackson2XmlView` uses the [Jackson XML extensions](https://github.com/FasterXML/jackson-dataformat-xml) `XmlMapper` to render the response content as XML. If the model contains multiple entries, you should explicitly set the object to be serialized by using the `modelKey` bean property. If the model contains a single entry, it is serialized automatically.
You can customized XML mapping as needed by using JAXB or Jacksons provided annotations. When you need further control, you can inject a custom `XmlMapper` through the `ObjectMapper` property, for cases where custom XML you need to provide serializers and deserializers for specific types.
## XML Marshalling
The `MarshallingView` uses an XML `Marshaller` (defined in the `org.springframework.oxm` package) to render the response content as XML. You can explicitly set the object to be marshalled by using a `MarshallingView` instances `modelKey` bean property. Alternatively, the view iterates over all model properties and marshals the first type that is supported by the `Marshaller`. For more information on the functionality in the `org.springframework.oxm` package, see [Marshalling XML using O/X Mappers](https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#oxm).
## XSLT Views
XSLT is a transformation language for XML and is popular as a view technology within web applications. XSLT can be a good choice as a view technology if your application naturally deals with XML or if your model can easily be converted to XML. The following section shows how to produce an XML document as model data and have it transformed with XSLT in a Spring Web MVC application.
This example is a trivial Spring application that creates a list of words in the `Controller` and adds them to the model map. The map is returned, along with the view name of our XSLT view. See [Annotated Controllers](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-controller) for details of Spring Web MVCs `Controller` interface. The XSLT controller turns the list of words into a simple XML document ready for transformation.
### Beans
Configuration is standard for a simple Spring web application: The MVC configuration has to define an `XsltViewResolver` bean and regular MVC annotation configuration. The following example shows how to do so:
Java
Kotlin
```java
@EnableWebMvc
@ComponentScan
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public XsltViewResolver xsltViewResolver() {
XsltViewResolver viewResolver = new XsltViewResolver();
viewResolver.setPrefix("/WEB-INF/xsl/");
viewResolver.setSuffix(".xslt");
return viewResolver;
}
}
```
### Controller
We also need a Controller that encapsulates our word-generation logic.
The controller logic is encapsulated in a `@Controller` class, with the handler method being defined as follows:
Java
Kotlin
```java
@Controller
public class XsltController {
@RequestMapping("/")
public String home(Model model) throws Exception {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = document.createElement("wordList");
List<String> words = Arrays.asList("Hello", "Spring", "Framework");
for (String word : words) {
Element wordNode = document.createElement("word");
Text textNode = document.createTextNode(word);
wordNode.appendChild(textNode);
root.appendChild(wordNode);
}
model.addAttribute("wordList", root);
return "home";
}
}
```
So far, we have only created a DOM document and added it to the Model map. Note that you can also load an XML file as a `Resource` and use it instead of a custom DOM document.
There are software packages available that automatically 'domify' an object graph, but, within Spring, you have complete flexibility to create the DOM from your model in any way you choose. This prevents the transformation of XML playing too great a part in the structure of your model data, which is a danger when using tools to manage the DOMification process.
### Transformation
Finally, the `XsltViewResolver` resolves the “home” XSLT template file and merges the DOM document into it to generate our view. As shown in the `XsltViewResolver` configuration, XSLT templates live in the `war` file in the `WEB-INF/xsl` directory and end with an `xslt` file extension.
The following example shows an XSLT transform:
```xml
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:template match="/">
<html>
<head><title>Hello!</title></head>
<body>
<h1>My First Words</h1>
<ul>
<xsl:apply-templates/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="word">
<li><xsl:value-of select="."/></li>
</xsl:template>
</xsl:stylesheet>
```
The preceding transform is rendered as the following HTML:
```html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Hello!</title>
</head>
<body>
<h1>My First Words</h1>
<ul>
<li>Hello</li>
<li>Spring</li>
<li>Framework</li>
</ul>
</body>
</html>
```
## 参考资料
- [Spring Framework 官方文档](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/index.html)
- [Spring Framework 官方文档之 Web](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html)

View File

@ -25,7 +25,7 @@ hidden: true
- [Spring WebMvc](01.SpringWebMvc.md)
- [DispatcherServlet](02.DispatcherServlet.md)
- [过滤器](03.Spring过滤器.md)
- [Controller](04.Controller.md)
- [SprngWeb 组件](04.SprngWeb组件.md)
- [跨域](05.Spring跨域.md)
- 视图技术
- Spring MVC 配置