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 還提供了一些特性,使自定義此行為變得更容易。

你可以透過環境變數配置 ObjectMapperXmlMapper 例項。Jackson 提供了廣泛的開/關特性集,可用於配置其處理的各個方面。這些特性在 Jackson 中的幾個列舉中描述,並對映到環境變數中的屬性

列舉 屬性

EnumFeature

spring.jackson.datatype.enum.<feature_name>

true, false

JsonNodeFeature

spring.jackson.datatype.json-node.<feature_name>

true, false

DeserializationFeature

spring.jackson.deserialization.<feature_name>

true, false

JsonGenerator.Feature

spring.jackson.generator.<feature_name>

true, false

MapperFeature

spring.jackson.mapper.<feature_name>

true, false

JsonParser.Feature

spring.jackson.parser.<feature_name>

true, false

SerializationFeature

spring.jackson.serialization.<feature_name>

true, false

JsonInclude.Include

spring.jackson.default-property-inclusion

always, non_null, non_absent, non_default, non_empty

例如,要啟用美觀列印(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 宣告一個型別為 @BeanServletServletRegistrationBean,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.prefixspring.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.prefixspring.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 模板。通常,您需要一個名稱解析為 errorView 或一個處理 /error 路徑的 @Controller。除非您替換了一些預設配置,否則您應該在您的 ApplicationContext 中找到一個 BeanNameViewResolver,因此一個名為 error@Bean 將是一種實現方式。有關更多選項,請參見 ErrorMvcAutoConfiguration

另請參見關於錯誤處理的章節,瞭解如何在 servlet 容器中註冊處理程式。