SpringBoot内容协商机制

内容协商(Content Negotiation)是Spring MVC提供的一种机制,它允许客户端和服务器协商响应的内容类型。Spring Boot在此基础上进行了自动配置和简化。


1、内容协商基本概念

内容协商机制主要涉及以下三个方面:

  1. 客户端通过Accept头指定希望接收的响应格式
  2. 服务器根据请求和自身能力决定返回的格式
  3. 服务器通过Content-Type头告知客户端返回的内容格式

2、Spring Boot内容协商实现方式

Spring Boot支持以下几种内容协商方式:

基于HTTP Accept头

这是最标准的内容协商方式,客户端在请求头中指定Accept,如:

java 复制代码
Accept: application/json

基于URL后缀

通过在URL路径中添加后缀来指定格式,如:

  • /api/user.json - 返回JSON格式
  • /api/user.xml - 返回XML格式

基于请求参数

通过请求参数指定格式,默认参数名为format,如:

  • /api/user?format=json

3、Spring Boot内容协商配置

默认配置

Spring Boot默认启用了基于Accept头和基于后缀的内容协商,支持JSON格式(需要Jackson库)和XML格式(需要Jackson XML扩展或JAXB)。

自定义配置

可以在application.propertiesapplication.yml中进行配置:

properties 复制代码
# 启用/禁用基于后缀的内容协商
spring.mvc.contentnegotiation.favor-path-extension=true
# 启用/禁用基于参数的内容协商
spring.mvc.contentnegotiation.favor-parameter=false
# 自定义参数名称(如果启用基于参数的内容协商)
spring.mvc.contentnegotiation.parameter-name=format
# 注册的媒体类型映射
spring.mvc.contentnegotiation.media-types.pdf=application/pdf

4、代码示例

基本控制器示例:

java 复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return new User(id, "张三", "zhangsan@example.com");
    }

    @GetMapping(value = "/{id}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
    public User getUserWithProduces(@PathVariable Long id) {
        return new User(id, "李四", "lisi@example.com");
    }
}
// User实体类
@Data
public class User {
    private Long id;
    private String name;
    private String email;
}

自定义HttpMessageConverter:

如果你想支持自定义格式(如PDF),可以这样做:

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer
            .favorPathExtension(true)
            .favorParameter(true)
            .parameterName("mediaType")
            .ignoreAcceptHeader(false)
            .useRegisteredExtensionsOnly(false)
            .defaultContentType(MediaType.APPLICATION_JSON)
            .mediaType("json", MediaType.APPLICATION_JSON)
            .mediaType("xml", MediaType.APPLICATION_XML);
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 添加自定义的MessageConverter
        converters.add(new PdfMessageConverter());
    }
}
// 自定义PDF转换器
public class PdfMessageConverter extends AbstractHttpMessageConverter<User> {

    public PdfMessageConverter() {
        super(new MediaType("application", "pdf"));
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return User.class.isAssignableFrom(clazz);
    }

    @Override
    protected User readInternal(Class<? extends User> clazz, HttpInputMessage inputMessage) {
        throw new UnsupportedOperationException("Not implemented");
    }

    @Override
    protected void writeInternal(User user, HttpOutputMessage outputMessage) {
        // 实现将User对象转换为PDF的逻辑
        // 这里只是示例,实际需要PDF生成库如iText
        try {
            outputMessage.getBody().write(("PDF Report for User: " + user.getName()).getBytes());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

使用不同格式请求

JSON格式请求
复制代码
GET /api/users/1 HTTP/1.1
Accept: application/json

复制代码
GET /api/users/1.json HTTP/1.1
XML格式请求
复制代码
GET /api/users/1 HTTP/1.1
Accept: application/xml

复制代码
GET /api/users/1.xml HTTP/1.1
PDF格式请求
复制代码
GET /api/users/1 HTTP/1.1
Accept: application/pdf

复制代码
GET /api/users/1.pdf HTTP/1.1

5、内容协商的工作原理

  1. ContentNegotiationManager负责确定请求的媒体类型
  2. 根据配置(Accept头、路径后缀、参数)确定最匹配的媒体类型
  3. 查找能够处理该媒体类型的HttpMessageConverter
  4. 使用找到的转换器进行响应内容的序列化

6、常见问题解决

1. 返回格式不符合预期

确保:

  • 添加了相应的依赖(如Jackson XML或JAXB)
  • 正确设置了Accept
  • 控制器方法上使用produces限定了支持的媒体类型

2. 添加自定义格式

需要:

  1. 实现自定义的HttpMessageConverter
  2. 注册媒体类型映射
  3. 确保转换器被添加到转换器列表中

7、最佳实践

  1. REST API优先使用基于Accept头的内容协商
  2. 对于浏览器友好的API,可以同时支持基于后缀的内容协商
  3. 明确指定控制器支持的媒体类型(使用produces)
  4. 为自定义格式提供合适的HttpMessageConverter
相关推荐
数据皮皮侠AI3 天前
中国土地利用驱动因子数据集(9种驱动因子/裁剪到省市/Tif)
大数据·人工智能·笔记·能源·1024程序员节
数据皮皮侠AI7 天前
上市公司耐心资本数据(2010-2025)
大数据·人工智能·笔记·能源·1024程序员节
开开心心就好9 天前
解决图片无页码添加功能的实用工具
javascript·python·安全·智能手机·pdf·音视频·1024程序员节
学传打活10 天前
【边打字.边学昆仑正义文化】_25_宇宙动植物的由来(1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
开开心心就好14 天前
用户推荐的文件解锁与强制操作工具
安全·智能手机·pdf·scala·音视频·symfony·1024程序员节
liguojun202519 天前
软硬一体智慧场馆系统推荐——助力场馆数字化高效升级
java·大数据·人工智能·物联网·1024程序员节
开开心心就好1 个月前
吾爱大佬原创的文件时间修改工具
安全·智能手机·pdf·电脑·智能音箱·智能手表·1024程序员节
开开心心就好1 个月前
近200个工具的电脑故障修复合集
安全·智能手机·pdf·电脑·consul·memcache·1024程序员节
数据皮皮侠AI1 个月前
中国城市可再生能源数据集(2005-2021)|顶刊 Sci Data 11 种能源面板
大数据·人工智能·笔记·能源·1024程序员节
计算机毕业论文辅导1 个月前
物联网实战:基于MQTT协议的智能家居数据传输系统设计与实现
1024程序员节