Spring MVC
Spring Boot 有许多包含 Spring MVC 的 starter。请注意,有些 starter 包含对 Spring MVC 的依赖,而不是直接包含它。本节回答了关于 Spring MVC 和 Spring Boot 的常见问题。
编写 JSON REST 服务
Spring Boot 应用程序中的任何 Spring @RestController 只要 Jackson2 在类路径上,默认情况下都应呈现 JSON 响应,如下例所示
-
Java
-
Kotlin
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping("/thing")
public MyThing thing() {
return new MyThing();
}
}
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class MyController {
@RequestMapping("/thing")
fun thing(): MyThing {
return MyThing()
}
}
只要 MyThing 可以通过 Jackson2 序列化(对于普通的 POJO 或 Groovy 对象而言为 true),那么 localhost:8080/thing 默认情况下就会提供它的 JSON 表示。请注意,在浏览器中,有时可能会看到 XML 响应,因为浏览器倾向于发送偏好 XML 的 Accept 头。
编写 XML REST 服务
如果类路径上有 Jackson XML 扩展 (jackson-dataformat-xml),你可以使用它来呈现 XML 响应。我们用于 JSON 的前一个示例也可以工作。要使用 Jackson XML 渲染器,请将以下依赖项添加到你的项目:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
如果 Jackson 的 XML 扩展不可用而 JAXB 可用,则可以使用 XML 呈现,但附加要求是 MyThing 注解为 @XmlRootElement,如下例所示
-
Java
-
Kotlin
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class MyThing {
private String name;
// getters/setters ...
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
import jakarta.xml.bind.annotation.XmlRootElement
@XmlRootElement
class MyThing {
var name: String? = null
}
你需要确保 JAXB 库是你项目的一部分,例如通过添加
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
要让服务器呈现 XML 而不是 JSON,你可能需要发送一个 Accept: text/xml 头(或使用浏览器)。 |
自定义 Jackson ObjectMapper
Spring MVC(客户端和服务端)使用 HttpMessageConverters 在 HTTP 交互中协商内容转换。如果 Jackson 在类路径上,你已经会获得 Jackson2ObjectMapperBuilder 提供的默认转换器,它的一个实例会为你自动配置。
默认创建的 ObjectMapper 实例(或 Jackson XML 转换器的 XmlMapper)具有以下自定义属性
-
MapperFeature.DEFAULT_VIEW_INCLUSION被禁用 -
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES被禁用 -
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS被禁用 -
SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS被禁用
Spring Boot 还提供了一些特性,使自定义此行为变得更容易。
你可以通过环境变量配置 ObjectMapper 和 XmlMapper 实例。Jackson 提供了广泛的开/关特性集,可用于配置其处理的各个方面。这些特性在 Jackson 中的几个枚举中描述,并映射到环境变量中的属性
| 枚举 | 属性 | 值 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
例如,要启用美观打印(pretty print),请设置 spring.jackson.serialization.indent_output=true。请注意,得益于 relaxed binding 的使用,indent_output 的大小写不必与相应的枚举常量 INDENT_OUTPUT 的大小写匹配。
此基于环境的配置应用于自动配置的 Jackson2ObjectMapperBuilder Bean,并应用于使用此 builder 创建的任何 mapper,包括自动配置的 ObjectMapper Bean。
上下文的 Jackson2ObjectMapperBuilder 可以通过一个或多个 Jackson2ObjectMapperBuilderCustomizer Bean 进行自定义。这些 customizer Bean 可以进行排序(Boot 自己的 customizer 的顺序为 0),从而可以在 Boot 的自定义之前和之后应用额外的自定义。
任何类型为 Module 的 Bean 都会自动注册到自动配置的 Jackson2ObjectMapperBuilder 中,并应用于它创建的任何 ObjectMapper 实例。这提供了一个全局机制,可在你向应用程序添加新功能时贡献自定义模块。
如果你想完全替换默认的 ObjectMapper,可以定义一个该类型的 @Bean,或者如果你更喜欢基于 builder 的方法,可以定义一个 Jackson2ObjectMapperBuilder @Bean。在定义 ObjectMapper Bean 时,建议将其标记为 @Primary,因为它将替换的自动配置的 ObjectMapper 也是 @Primary。请注意,在任何一种情况下,这样做都会禁用 ObjectMapper 的所有自动配置。
如果你提供了任何类型为 MappingJackson2HttpMessageConverter 的 @Beans,它们将替换 MVC 配置中的默认值。此外,还提供了一个类型为 HttpMessageConverters 的便捷 Bean(如果你使用默认的 MVC 配置,此 Bean 始终可用)。它有一些有用的方法可以访问默认和用户增强的消息转换器。
有关更多详细信息,请参阅自定义 @ResponseBody 渲染部分以及WebMvcAutoConfiguration 源代码。
自定义 @ResponseBody 渲染
Spring 使用 HttpMessageConverters 来渲染 @ResponseBody(或来自 @RestController 的响应)。你可以通过在 Spring Boot 上下文中添加适当类型的 Bean 来贡献额外的转换器。如果你添加的 Bean 是默认情况下也会包含的类型(例如用于 JSON 转换的 MappingJackson2HttpMessageConverter),它将替换默认值。还提供了一个类型为 HttpMessageConverters 的便捷 Bean,如果你使用默认的 MVC 配置,此 Bean 始终可用。它有一些有用的方法可以访问默认和用户增强的消息转换器(例如,如果你想将它们手动注入自定义的 RestTemplate,这可能很有用)。
与正常的 MVC 用法一样,你提供的任何 WebMvcConfigurer Bean 也可以通过覆盖 configureMessageConverters 方法来贡献转换器。然而,与正常的 MVC 不同,你只能提供你需要的额外转换器(因为 Spring Boot 使用相同的机制来贡献其默认值)。最后,如果你通过提供自己的 @EnableWebMvc 配置来选择退出默认的 Spring Boot MVC 配置,你可以完全控制并使用 WebMvcConfigurationSupport 中的 getMessageConverters 手动完成所有操作。
有关更多详细信息,请参见WebMvcAutoConfiguration 源代码。
处理 Multipart 文件上传
Spring Boot 采用 servlet 5 的 Part API 来支持文件上传。默认情况下,Spring Boot 配置 Spring MVC 时,单个文件的最大大小为 1MB,单次请求中的文件数据总量最大为 10MB。你可以使用 MultipartProperties 类中公开的属性来覆盖这些值,包括存储中间数据的 location(例如,到 /tmp 目录)以及数据刷新到磁盘的 threshold。例如,如果你想指定文件大小不受限制,请将 spring.servlet.multipart.max-file-size 属性设置为 -1。
当你希望在 Spring MVC 控制器处理方法中接收 multipart 编码的文件数据作为 @RequestParam 注解的 MultipartFile 类型参数时,multipart 支持会很有帮助。
有关更多详细信息,请参阅MultipartAutoConfiguration 源代码。
| 建议使用容器内置的 multipart 上传支持,而不是引入像 Apache Commons File Upload 这样的额外依赖项。 |
关闭 Spring MVC DispatcherServlet
默认情况下,所有内容都从应用程序的根路径 (/) 提供服务。如果你想映射到不同的路径,可以按如下方式配置
-
属性
-
YAML
spring.mvc.servlet.path=/mypath
spring:
mvc:
servlet:
path: "/mypath"
如果你有额外的 servlet,可以为每个 servlet 声明一个类型为 @Bean 的 Servlet 或 ServletRegistrationBean,Spring Boot 会将其透明地注册到容器中。由于 servlet 以这种方式注册,它们可以映射到 DispatcherServlet 的子上下文而无需调用 DispatcherServlet 本身。
自己配置 DispatcherServlet 是不寻常的,但如果你确实需要这样做,还必须提供一个类型为 DispatcherServletPath 的 @Bean 来提供你自定义的 DispatcherServlet 的路径。
关闭默认 MVC 配置
完全控制 MVC 配置的最简单方法是提供带有 @Configuration 注解的自己的 @EnableWebMvc。这样做会将所有 MVC 配置权交给你。
自定义 ViewResolver
一个 ViewResolver 是 Spring MVC 的核心组件,它将 @Controller 中的视图名称转换为实际的 View 实现。请注意,视图解析器主要用于 UI 应用程序,而不是 REST 风格的服务(View 不用于渲染 @ResponseBody)。有许多 ViewResolver 实现可供选择,Spring 本身对此没有主观意见,不强制你使用哪一个。另一方面,Spring Boot 会为你安装一个或两个,具体取决于它在类路径和应用程序上下文中找到的内容。DispatcherServlet 使用它在应用程序上下文中找到的所有解析器,依次尝试每个解析器直到获得结果。如果你添加自己的解析器,则必须注意其顺序以及添加的位置。
WebMvcAutoConfiguration 将以下 ViewResolver Bean 添加到你的上下文中
-
一个名为 ‘defaultViewResolver’ 的
InternalResourceViewResolver。它定位可以使用DefaultServlet渲染的物理资源(包括静态资源和 JSP 页面,如果你使用这些)。它将前缀和后缀应用于视图名称,然后在 servlet 上下文中查找具有该路径的物理资源(默认值都为空,但可以通过spring.mvc.view.prefix和spring.mvc.view.suffix进行外部配置)。你可以通过提供相同类型的 Bean 来覆盖它。 -
一个名为 ‘beanNameViewResolver’ 的
BeanNameViewResolver。这是视图解析器链中一个有用的成员,它会查找与正在解析的View同名的任何 Bean。通常不需要覆盖或替换它。 -
只有当实际存在类型为
View的 Bean 时,才会添加一个名为 ‘viewResolver’ 的ContentNegotiatingViewResolver。这是一个复合解析器,它委托给所有其他解析器,并尝试找到与客户端发送的 ‘Accept’ HTTP 头匹配的解析器。有一篇关于 ContentNegotiatingViewResolver 的有用博客,你可以学习它以了解更多信息,你也可以查看源代码以获取详细信息。你可以通过定义一个名为 ‘viewResolver’ 的 Bean 来关闭自动配置的ContentNegotiatingViewResolver。 -
如果你使用 Thymeleaf,你还有一个名为 ‘thymeleafViewResolver’ 的
ThymeleafViewResolver。它通过在视图名称周围添加前缀和后缀来查找资源。前缀是spring.thymeleaf.prefix,后缀是spring.thymeleaf.suffix。前缀和后缀的默认值分别为 ‘classpath:/templates/’ 和 ‘.html’。你可以通过提供同名 Bean 来覆盖ThymeleafViewResolver。 -
如果你使用 FreeMarker,你还有一个名为 ‘freeMarkerViewResolver’ 的
FreeMarkerViewResolver。它通过在视图名称周围添加前缀和后缀来在加载路径(外部化为spring.freemarker.templateLoaderPath,默认值为 ‘classpath:/templates/’)中查找资源。前缀外部化为spring.freemarker.prefix,后缀外部化为spring.freemarker.suffix。前缀和后缀的默认值分别为 空 和 ‘.ftlh’。你可以通过提供同名 Bean 来覆盖FreeMarkerViewResolver。FreeMarker 变量可以通过定义一个类型为FreeMarkerVariablesCustomizer的 Bean 进行自定义。 -
如果您使用 Groovy 模板(实际上,如果您的 classpath 中包含
groovy-templates),您还会有一个名为 ‘groovyMarkupViewResolver’ 的GroovyMarkupViewResolver。它通过在视图名称周围添加前缀和后缀来查找加载器路径中的资源(外部化为spring.groovy.template.prefix和spring.groovy.template.suffix)。前缀和后缀的默认值分别是 ‘classpath:/templates/’ 和 ‘.tpl’。您可以通过提供同名的 bean 来覆盖GroovyMarkupViewResolver。 -
如果您使用 Mustache,您还会有一个名为 ‘mustacheViewResolver’ 的
MustacheViewResolver。它通过在视图名称周围添加前缀和后缀来查找资源。前缀是spring.mustache.prefix,后缀是spring.mustache.suffix。前缀和后缀的默认值分别是 ‘classpath:/templates/’ 和 ‘.mustache’。您可以通过提供同名的 bean 来覆盖MustacheViewResolver。
更多详情,请参见以下章节
定制“whitelabel”(白标)错误页面
Spring Boot 安装了一个“whitelabel”(白标)错误页面,当您遇到服务器错误时,浏览器客户端会看到该页面(消费 JSON 和其他媒体类型的机器客户端应看到带有正确错误代码的合理响应)。
将 server.error.whitelabel.enabled=false 设置为关闭默认错误页面。这样做会恢复您正在使用的 servlet 容器的默认行为。请注意,Spring Boot 仍然会尝试解析错误视图,因此您最好添加自己的错误页面,而不是完全禁用它。 |
使用您自己的页面覆盖错误页面取决于您使用的模板技术。例如,如果您使用 Thymeleaf,可以添加一个 error.html 模板。如果您使用 FreeMarker,可以添加一个 error.ftlh 模板。通常,您需要一个名称解析为 error 的 View 或一个处理 /error 路径的 @Controller。除非您替换了一些默认配置,否则您应该在您的 ApplicationContext 中找到一个 BeanNameViewResolver,因此一个名为 error 的 @Bean 将是一种实现方式。有关更多选项,请参见 ErrorMvcAutoConfiguration。
另请参见关于错误处理的章节,了解如何在 servlet 容器中注册处理程序。