要了解Spring MVC的內容協商機制,先要了解HTTP的內容協商機制,SpringMVC實現了HTTP內容協商的同時,又進行了擴展。
HTTP內容協商
一個URL的資源服務端可以有多種響應形式,即MIME(Media Type)媒體類型。但客戶端只需要一種,這就要求客戶端和服務端之間有一種機制,能確保服務端響應的是客戶端想要的,這就是內容協商。
內容協商通常有兩種方式,第一是服務端將可用列表發給客戶端,客戶端選擇之后服務端再發送過來,這種方式會多一次網絡交互,而且普通用戶不太可能了解技術性的選項,所以這種方式一般不用。第二種方式是常用的,客戶端發送請求時指明需要的MIME,比如HTTP首部的Accept;服務端根據客戶端的要求返回對應的內容形式,並在響應頭中說明,比如Content-Type。詳見下表:
請求頭 | 請求頭說明 | 響應頭 | 響應頭說明 |
Accept | 告訴服務端需要的MIME | Content-Type | 告訴客戶端響應的媒體類型 |
Accept-Language | 告訴服務端需要的語言 | Content-Language | 告訴客戶端響應的語言 |
Accept-Charset | 告訴服務端需要的字符集 | Content-Charset | 告訴客戶端響應的字符集 |
Accept-Encoding | 告訴服務端需要的壓縮方式 | Content-Encoding | 告訴客戶端響應的壓縮方式 |
先看個請求首部的例子:
首先解釋一下q,權重的意思,最高為1,最低為0,默認是1。
Accept:*/*表示可以是任何MIME資源,其他的比如text/plain,text/html等。
Accept-Encoding:壓縮方式可以是gzip,deflate,br。服務端向客戶端發送的資源可通過壓縮減少傳輸量。
Accept-Language:中文的權重最高。這里瀏覽器可以根據操作系統的語言或者瀏覽器本身的語言設置來選擇,但能否協商成功還要看服務端是否支持多語言。
再看個響應首部的例子:
Content-Encoding:說明壓縮的方式是gzip。
Content-Type:表示MIME是html文本,字符集是utf-8
Spring MVC的內容協商
Spring MVC支持4種內容協商方式:HTTP首部Accept,擴展名,請求參數,或者固定類型。我們通過一個例子來分別驗證。
@RestController @RequestMapping("/users") public class UserController { @RequestMapping("{id}") public User get(@PathVariable Integer id) { return new User(id, "呵呵"); } }
public class User {
private Integer id;
private String name;
User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
為了驗證json和xml兩種MIME,我們需要用到下面兩個jar包,SpringMVC在轉換json和xml時MessageConvertor默認的兩個jar依賴
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.6.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.9.9</version> </dependency>
Accept
在瀏覽器中輸入http://localhost:8080/mvc/users/1,響應結果是:
<User><id>1</id><name>呵呵</name></User>
可以看到返回了xml格式的數據
再用Postman測試
返回了json格式的數據,為什么和瀏覽器不一樣呢?其實就是權重q的設置問題。
瀏覽器的Accept設置:

由於我們用了@RestController注解,不會返回一個View,在瀏覽器請求的所有Accept中不能返回text/html,application/xhtml+xml格式,就取了權重是0.9的application/xml返回了。
而在Postman的請求中,Accept為任意格式,Spring MVC返回了json格式數據,由此可驗證json的優先級比xml高。
那么怎么在Postman中返回xml格式的數據呢,修改Accept值就可以了:
我們已經驗證了Spring MVC完全支持基於HTTP Accept首部的內容協商機制了。
擴展名
我們可以通過設置url的擴展名來指定需要的MIME,如果加了Spring MVC可以識別的擴展名將會忽略Accept的值。
在http://localhost:8080/mvc/users/1后面分別加上.json和.xml可得到對應格式的返回數據,就不貼圖了。重點看下擴展名和Accept的優先級:
由此可驗證擴展名優先級比Accept要高。
請求參數
請求參數內容協商機制默認是關閉的,我們手動打開:
@Configuration @EnableWebMvc @ComponentScan("com.acwei.spring.mvc") public class MvcConfiguration extends WebMvcConfigurerAdapter { @Bean public LogInterceptor logInterceptor() { return new LogInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(logInterceptor()).addPathPatterns("/**"); } @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.favorParameter(true); } }
在configureContentNegotiation方法中打開請求參數內容協商設置,注意:請求參數機制優先級低於擴展名,所以我們驗證時候先把url后綴去掉:
可以看到請求參數的優先級高於Accept。綜合上面的實驗可以得出幾種機制的優先級:后綴 > 請求參數 > HTTP首部Accept。
固定類型
最后一種就是@RequestMapping注解屬性produces:
響應的MIME在這里指定,需要說明的是,這里指定的類型不能和后綴、請求參數、Accept沖突。比如這里指定了json格式,那么后綴如果不是json,或者format不是json,或者Accept不是application/json、*/*,將無法完成內容協商,http status code為406。