ContentNegotiationStrategy
//WebMvcConfigurationSupport#mvcContentNegotiationManager
public ContentNegotiationManager mvcContentNegotiationManager() {
if (this.contentNegotiationManager == null) {
ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext);
configurer.mediaTypes(getDefaultMediaTypes());
configureContentNegotiation(configurer);
this.contentNegotiationManager = configurer.buildContentNegotiationManager();
}
return this.contentNegotiationManager;
}
//WebMvcConfigurationSupport#getDefaultMediaTypes
protected Map<String, MediaType> getDefaultMediaTypes() {
Map<String, MediaType> map = new HashMap<>(4);
if (romePresent) {
map.put("atom", MediaType.APPLICATION_ATOM_XML);
map.put("rss", MediaType.APPLICATION_RSS_XML);
}
if (!shouldIgnoreXml && (jaxb2Present || jackson2XmlPresent)) {
map.put("xml", MediaType.APPLICATION_XML);
}
if (jackson2Present || gsonPresent || jsonbPresent) {
map.put("json", MediaType.APPLICATION_JSON);
}
if (jackson2SmilePresent) {
map.put("smile", MediaType.valueOf("application/x-jackson-smile"));
}
if (jackson2CborPresent) {
map.put("cbor", MediaType.APPLICATION_CBOR);
}
return map;
}
//ContentNegotiationConfigurer#mediaTypes
public ContentNegotiationConfigurer mediaTypes(@Nullable Map<String, MediaType> mediaTypes) {
if (mediaTypes != null) {
this.mediaTypes.putAll(mediaTypes);
}
return this;
}
//ContentNegotiationConfigurer#buildContentNegotiationManager
protected ContentNegotiationManager buildContentNegotiationManager() {
this.factory.addMediaTypes(this.mediaTypes);
return this.factory.build();
}
//ContentNegotiationManagerFactoryBean#build
public ContentNegotiationManager build() {
List<ContentNegotiationStrategy> strategies = new ArrayList<>();
if (this.strategies != null) {
//...
}
else {
//...
if (this.favorParameter) {
ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(this.mediaTypes);
strategy.setParameterName(this.parameterName);
if (this.useRegisteredExtensionsOnly != null) {
//...
}
else {
strategy.setUseRegisteredExtensionsOnly(true); // backwards compatibility
}
strategies.add(strategy);
}
if (!this.ignoreAcceptHeader) {
strategies.add(new HeaderContentNegotiationStrategy());
}
//...
}
this.contentNegotiationManager = new ContentNegotiationManager(strategies);
//...
return this.contentNegotiationManager;
}
ContentNegotiationManager(内容协商管理器)中有两个ContentNegotiationStrategy(内容协商策略):
- 基于请求头的内容协商策略(HeaderContentNegotiationStrategy)
- 基于请求参数的内容协商策略(ParameterContentNegotiationStrategy)。其中,parameterName(参数名)固定为
format
,支持的MediaTypes (媒体类型)有:
1)xml
,(xml->application/xml)
2)json
,(json->application/json)
自定义ContentNegotiationStrategy
- com.example.boot下新建类:bean.Person。
package com.example.boot.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Person {
private String name;
private Integer age;
private Date birth;
}
- com.example.boot下新建控制器:controller.HelloController。
package com.example.boot.controller;
import com.example.boot.bean.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date;
@Controller
public class HelloController {
@GetMapping("/person")
@ResponseBody
public Person person(){
Person person = new Person("Tom", 1, new Date());
return person;
}
}
- com.example.boot下新建消息转换器(HttpMessageConverter):converter.HelloMessageConverter。
package com.example.boot.converter;
import com.example.boot.bean.Person;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
public class HelloMessageConverter implements HttpMessageConverter<Person> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return clazz.isAssignableFrom(Person.class);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/x-helloworld");
}
@Override
public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
String data = person.getName()+";"+person.getAge()+";"+person.getBirth();
OutputStream body = outputMessage.getBody();
body.write(data.getBytes());
}
}
- com.example.boot下新建配置类:config.MyConfig。
package com.example.boot.config;
import com.example.boot.converter.HelloMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new HelloMessageConverter());
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaTypes = new HashMap<>();
mediaTypes.put("json",MediaType.APPLICATION_JSON);
mediaTypes.put("xml",MediaType.APPLICATION_XML);
mediaTypes.put("hello",MediaType.parseMediaType("application/x-helloworld"));
ParameterContentNegotiationStrategy parameterContentNegotiationStrategy = new ParameterContentNegotiationStrategy(mediaTypes);
HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderContentNegotiationStrategy();
configurer.strategies(Arrays.asList(parameterContentNegotiationStrategy,headerContentNegotiationStrategy));
}
}
- resources.static下新建静态页面:index.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<a href="/person?format=json">format=json</a><br/>
<a href="/person?format=xml">format=xml</a><br/>
<a href="/person?format=hello">format=hello</a>
</body>
</html>
- resources下创建配置文件application.yml。
spring:
mvc:
contentnegotiation:
favor-parameter: true
- 启动应用,通过浏览器或IDEA http方式访问接口localhost:8080/person,效果如下所示。
Accept: application/json时或format=json时,接口返回
{
"name": "Tom",
"age": 1,
"birth": "2022-01-10T08:44:59.748+00:00"
}
Accept: application/xml时或format=xml时,接口返回
<Person>
<name>Tom</name>
<age>1</age>
<birth>2022-01-10T08:45:28.795+00:00</birth>
</Person>
Accept: application/x-helloworld时或format=hello时,接口返回
Tom;1;Mon Jan 10 16:45:46 CST 2022