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 容器中注册处理程序。