Spring MVC

Spring Boot 有許多 starter 包含了 Spring MVC。請注意,有些 starter 包含對 Spring MVC 的依賴,而不是直接包含它。本節回答關於 Spring MVC 和 Spring Boot 的常見問題。

撰寫 JSON REST 服務

Spring Boot 應用程式中的任何 Spring @RestController 預設應呈現 JSON 回應,只要 classpath 上有 Jackson2 即可,如下例所示

  • 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 物件來說是如此),那麼 localhost:8080/thing 就會預設提供它的 JSON 表示。請注意,在瀏覽器中,您有時可能會看到 XML 回應,因為瀏覽器傾向於發送偏好 XML 的 accept header。

撰寫 XML REST 服務

如果您的 classpath 上有 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 header(或使用瀏覽器)。

自訂 Jackson ObjectMapper

Spring MVC(客戶端和伺服器端)使用 HttpMessageConverters 來協商 HTTP 交換中的內容轉換。如果 Jackson 在 classpath 上,您已經獲得了 Jackson2ObjectMapperBuilder 提供的預設轉換器,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 提供了一套廣泛的 on/off 功能,可用於組態其處理的各個方面。這些功能在(Jackson 中的)幾個枚舉中描述,這些枚舉映射到環境變數中的屬性

枚舉 屬性

com.fasterxml.jackson.databind.cfg.EnumFeature

spring.jackson.datatype.enum.<feature_name>

true, false

com.fasterxml.jackson.databind.cfg.JsonNodeFeature

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

true, false

com.fasterxml.jackson.databind.DeserializationFeature

spring.jackson.deserialization.<feature_name>

true, false

com.fasterxml.jackson.core.JsonGenerator.Feature

spring.jackson.generator.<feature_name>

true, false

com.fasterxml.jackson.databind.MapperFeature

spring.jackson.mapper.<feature_name>

true, false

com.fasterxml.jackson.core.JsonParser.Feature

spring.jackson.parser.<feature_name>

true, false

com.fasterxml.jackson.databind.SerializationFeature

spring.jackson.serialization.<feature_name>

true, false

com.fasterxml.jackson.annotation.JsonInclude.Include

spring.jackson.default-property-inclusion

always, non_null, non_absent, non_default, non_empty

例如,若要啟用美化列印,請設定 spring.jackson.serialization.indent_output=true。請注意,由於使用了寬鬆綁定indent_output 的大小寫不必與對應的枚舉常數 INDENT_OUTPUT 的大小寫相符。

此基於環境變數的組態適用於自動組態的 Jackson2ObjectMapperBuilder bean,並適用於使用 builder 建立的任何 mapper,包括自動組態的 ObjectMapper bean。

context 的 Jackson2ObjectMapperBuilder 可以透過一個或多個 Jackson2ObjectMapperBuilderCustomizer beans 進行自訂。此類 customizer beans 可以排序(Boot 自己的 customizer 的順序為 0),允許在 Boot 自訂之前和之後都應用額外的自訂。

任何類型為 com.fasterxml.jackson.databind.Module 的 beans 都會自動註冊到自動組態的 Jackson2ObjectMapperBuilder,並應用於它建立的任何 ObjectMapper 實例。當您向應用程式新增新功能時,這提供了一種全域機制來貢獻自訂模組。

如果您想要完全取代預設的 ObjectMapper,請定義該類型的 @Bean,或者,如果您喜歡基於 builder 的方法,請定義 Jackson2ObjectMapperBuilder @Bean。當定義 ObjectMapper bean 時,建議將其標記為 @Primary,因為它將取代的自動組態 ObjectMapper@Primary。請注意,在任何一種情況下,這樣做都會停用 ObjectMapper 的所有自動組態。

如果您提供任何類型為 MappingJackson2HttpMessageConverter@Beans,它們會取代 MVC 組態中的預設值。此外,還提供了一個方便的類型為 HttpMessageConverters 的 bean(如果您使用預設的 MVC 組態,則始終可用)。它有一些有用的方法可以存取預設和使用者增強的訊息轉換器。

請參閱自訂 @ResponseBody 渲染章節和 WebMvcAutoConfiguration 原始碼以取得更多詳細資訊。

自訂 @ResponseBody 渲染

Spring 使用 HttpMessageConverters 來渲染 @ResponseBody(或來自 @RestController 的回應)。您可以透過在 Spring Boot context 中新增適當類型的 beans 來貢獻額外的轉換器。如果您新增的 bean 的類型原本就會預設包含(例如 JSON 轉換的 MappingJackson2HttpMessageConverter),它會取代預設值。提供了一個方便的類型為 HttpMessageConverters 的 bean,如果您使用預設的 MVC 組態,則始終可用。它有一些有用的方法可以存取預設和使用者增強的訊息轉換器(例如,如果您想要手動將它們注入到自訂 RestTemplate 中,這可能會很有用)。

與正常的 MVC 用法一樣,您提供的任何 WebMvcConfigurer beans 也可以透過覆寫 configureMessageConverters 方法來貢獻轉換器。但是,與正常的 MVC 不同,您只能提供您需要的額外轉換器(因為 Spring Boot 使用相同的機制來貢獻其預設值)。最後,如果您透過提供您自己的 @EnableWebMvc 組態來選擇退出預設的 Spring Boot MVC 組態,您可以完全掌控並使用來自 WebMvcConfigurationSupportgetMessageConverters 手動完成所有操作。

請參閱WebMvcAutoConfiguration 原始碼以取得更多詳細資訊。

處理 Multipart 檔案上傳

Spring Boot 採用 servlet 5 jakarta.servlet.http.Part API 來支援上傳檔案。預設情況下,Spring Boot 將 Spring MVC 組態為每個檔案的最大大小為 1MB,單個請求中的檔案資料最大為 10MB。您可以使用 MultipartProperties 類別中公開的屬性來覆寫這些值、儲存中間資料的位置(例如,到 /tmp 目錄)以及將資料刷新到磁碟的閾值。例如,如果您想要指定檔案大小不受限制,請將 spring.servlet.multipart.max-file-size 屬性設定為 -1

當您想要在 Spring MVC 控制器處理方法中接收作為 MultipartFile 類型的 @RequestParam 註解參數的 multipart 編碼檔案資料時,multipart 支援非常有用。

請參閱 MultipartAutoConfiguration 原始碼以取得更多詳細資訊。

建議使用容器的內建 multipart 上傳支援,而不是引入額外的依賴項,例如 Apache Commons File Upload。

關閉 Spring MVC DispatcherServlet

預設情況下,所有內容都從應用程式的根目錄 (/) 提供。如果您寧願映射到不同的路徑,您可以如下組態一個路徑

  • 屬性

  • YAML

spring.mvc.servlet.path=/mypath
spring:
  mvc:
    servlet:
      path: "/mypath"

如果您有其他 servlet,您可以為每個 servlet 宣告一個 ServletServletRegistrationBean 類型的 @Bean,Spring Boot 會將它們透明地註冊到容器中。由於 servlet 是以這種方式註冊的,因此它們可以映射到 DispatcherServlet 的子 context,而無需調用它。

自行組態 DispatcherServlet 是不尋常的,但如果您真的需要這樣做,也必須提供一個 DispatcherServletPath 類型的 @Bean,以提供自訂 DispatcherServlet 的路徑。

關閉預設 MVC 組態

完全掌控 MVC 組態的最簡單方法是提供您自己的帶有 @EnableWebMvc 註解的 @Configuration。這樣做會將所有 MVC 組態都交給您處理。

自訂 ViewResolver

ViewResolver 是 Spring MVC 的核心組件,它將 @Controller 中的視圖名稱轉換為實際的 View 實作。請注意,ViewResolver 主要用於 UI 應用程式,而不是 REST 風格的服務(View 不用於渲染 @ResponseBody)。有多種 ViewResolver 實作可供選擇,Spring 本身對於您應該使用哪種實作沒有既定的看法。另一方面,Spring Boot 會為您安裝一到兩個,具體取決於它在 classpath 和應用程式 context 中找到的內容。DispatcherServlet 使用它在應用程式 context 中找到的所有解析器,依序嘗試每個解析器,直到獲得結果。如果您新增自己的解析器,您必須注意順序以及您的解析器新增的位置。

WebMvcAutoConfiguration 將以下 ViewResolver 新增到您的 context 中

  • 一個名為 ‘defaultViewResolver’ 的 InternalResourceViewResolver。這個解析器會尋找可以使用 DefaultServlet 渲染的物理資源(包括靜態資源和 JSP 頁面,如果您使用的話)。它會將前綴和後綴應用於視圖名稱,然後在 servlet context 中尋找具有該路徑的物理資源(預設值都為空,但可以透過 spring.mvc.view.prefixspring.mvc.view.suffix 進行外部組態)。您可以透過提供相同類型的 bean 來覆寫它。

  • 一個名為 ‘beanNameViewResolver’ 的 BeanNameViewResolver。這是視圖解析器鏈中一個有用的成員,它會拾取任何與要解析的 View 同名的 beans。應該不需要覆寫或取代它。

  • 只有在實際存在 View 類型的 beans 時,才會新增一個名為 ‘viewResolver’ 的 ContentNegotiatingViewResolver。這是一個複合解析器,它委派給所有其他解析器,並嘗試尋找與客戶端發送的 ‘Accept’ HTTP header 相符的項目。有一篇關於 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

  • 如果您使用 Groovy 模板(實際上,如果 groovy-templates 在您的 classpath 上),您也會有一個名為 ‘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’ 錯誤頁面

如果您遇到伺服器錯誤(機器客戶端使用 JSON 和其他媒體類型應會看到帶有正確錯誤代碼的合理回應),Spring Boot 會安裝一個 ‘whitelabel’ 錯誤頁面,您可以在瀏覽器客戶端中看到它。

設定 server.error.whitelabel.enabled=false 以關閉預設錯誤頁面。這樣做會恢復您正在使用的 servlet 容器的預設值。請注意,Spring Boot 仍然會嘗試解析錯誤視圖,因此您應該新增自己的錯誤頁面,而不是完全停用它。

使用您自己的錯誤頁面覆寫錯誤頁面取決於您使用的模板技術。例如,如果您使用 Thymeleaf,您可以新增一個 error.html 模板。如果您使用 FreeMarker,您可以新增一個 error.ftlh 模板。一般來說,您需要一個解析為名稱為 errorView 或一個處理 /error 路徑的 @Controller。除非您取代了一些預設組態,否則您應該在您的 ApplicationContext 中找到一個 BeanNameViewResolver,因此名為 error@Bean 將是一種方法。請參閱 ErrorMvcAutoConfiguration 以取得更多選項。

另請參閱關於 錯誤處理 的章節,以了解如何在 servlet 容器中註冊處理常式的詳細資訊。