ContentNegotiationStrategy与自定义ContentNegotiationStrategy

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(内容协商策略):

  1. 基于请求头的内容协商策略(HeaderContentNegotiationStrategy)
  2. 基于请求参数的内容协商策略(ParameterContentNegotiationStrategy)。其中,parameterName(参数名)固定为format,支持的MediaTypes (媒体类型)有:
    1)xml,(xml->application/xml)
    2)json,(json->application/json)
自定义ContentNegotiationStrategy
  1. 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;
}
  1. 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;
    }
}
  1. 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());
    }
}
  1. 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));
    }
}
  1. 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>
  1. resources下创建配置文件application.yml。
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
  1. 启动应用,通过浏览器或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
WebMvcConfigurer接口主要用于配置Spring MVC的行为。在Spring Boot 2.0之前,我们可以通过重写WebMvcConfigurerAdapter类的方法来添加自定义拦截器和消息转换器等。然而,在Spring Boot 2.0之后,WebMvcConfigurerAdapter类被标记为@Deprecated(弃用)。所以,为了实现自定义配置,官方推荐直接实现WebMvcConfigurer接口或继承WebMvcConfigurationSupport类。 如果我们想自定义消息转换器,可以使用ContentNegotiationConfigurer。ContentNegotiationConfigurer能够配置内容协商策略,例如选择哪种媒体类型(如JSONXML)来作为响应的内容格式。我们可以通过自定义ContentNegotiationConfigurer来注册自己的消息转换器。 另外,如果我们对Spring默认的URL解析方式不满意,我们可以通过配置PathMatchConfigurer来进行自定义。PathMatchConfigurer可以配置路径匹配的规则,例如是否允许后缀模式、是否允许尾部斜杠等。如果不配置这些选项,Spring会使用默认配置。具体的配置方式可以参考RequestMappingHandlerMapping和ContentNegotiationStrategy配置。 综上所述,WebMvcConfigurer接口依赖于ContentNegotiationConfigurer和PathMatchConfigurer来实现自定义配置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot中的WebMvcConfigurer配置接口的详解](https://blog.csdn.net/pan_junbiao/article/details/120039885)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringMVC源码系列之自定义WebMvcConfigurer](https://blog.csdn.net/Crabime/article/details/98207697)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值