Spring MVC 根据请求头 (如 Accept) 怎么返回 JSON 或 XML 数据?

Spring MVC 通过 内容协商 (Content Negotiation) 来根据客户端请求的 Accept 头决定返回 JSON、XML 还是其他格式的数据。

以下是核心机制和步骤:

  1. 客户端请求中的 Accept 头:

    • 客户端(如浏览器、curl、Postman等)在发起HTTP请求时,会通过 Accept 请求头告知服务器它期望接收的数据格式(MIME类型)。
    • 例如:
      • Accept: application/json (客户端期望JSON)
      • Accept: application/xml (客户端期望XML)
      • Accept: application/json, application/xml;q=0.9, */*;q=0.8 (客户端优先期望JSON,如果不行则XML,q是权重因子)
  2. Spring MVC 的 ContentNegotiationManager:

    • Spring MVC 内部使用 ContentNegotiationManager (或其策略实现) 来确定请求的目标媒体类型。
    • ContentNegotiationManager 会按照一定的优先级顺序尝试解析客户端的期望格式。默认情况下(尤其是在Spring Boot中),这个顺序通常是:
      1. URL 路径扩展名 (Path Extension): 检查URL末尾是否有如 .json.xml 的扩展名。例如:GET /api/users/1.json。 (可以通过配置禁用,因为有些人认为这不符合纯粹的RESTful风格,URL应标识资源而非其表示。)
      2. URL 参数 (Parameter): 检查URL中是否有名为 format (或其他可配置名称) 的参数。例如:GET /api/users/1?format=json。 (默认通常是关闭的,但可以配置启用。)
      3. Accept 请求头: 这是最常用且最符合HTTP规范的方式。 ContentNegotiationManager 会解析 Accept 头来确定客户端期望的媒体类型。
      4. 默认内容类型 (Default Content Type): 如果以上方法都无法确定,或者客户端没有发送明确的 Accept 头,Spring MVC 可能会使用预设的默认内容类型(例如,JSON)。
  3. @ResponseBodyHttpMessageConverter:

    • 当Controller方法被 @ResponseBody 注解标记(或者Controller类被 @RestController 注解标记,它包含了 @ResponseBody),Spring MVC 知道这个方法的返回值应该直接写入HTTP响应体中,而不是被视图解析器解析为视图名。
    • Spring MVC 维护了一个 HttpMessageConverter 列表。这些转换器负责将Java对象序列化为特定的HTTP响应体格式(如JSON、XML)以及将HTTP请求体反序列化为Java对象。
    • 常见的转换器有:
      • MappingJackson2HttpMessageConverter: 用于处理JSON,依赖Jackson库。
      • Jaxb2RootElementHttpMessageConverter: 用于处理XML,依赖JAXB API和实现。
      • MappingJackson2XmlHttpMessageConverter: 也是用于处理XML,但使用Jackson的XML模块。
  4. 工作流程总结:

    1. 客户端发送请求,包含 Accept 头 (例如, Accept: application/xml)。
    2. DispatcherServlet 将请求路由到相应的Controller方法。
    3. ContentNegotiationManager 解析请求(主要关注 Accept 头),确定目标媒体类型为 application/xml
    4. Controller方法执行完毕,返回一个Java对象 (例如,一个 User 对象)。
    5. 由于方法有 @ResponseBody,Spring MVC会查找一个能够将 User 对象转换为 application/xml 格式的 HttpMessageConverter
    6. 如果找到了合适的转换器(例如 Jaxb2RootElementHttpMessageConverterMappingJackson2XmlHttpMessageConverter),该转换器就会将 User 对象序列化为XML字符串。
    7. Spring MVC将序列化后的XML字符串写入HTTP响应体,并设置响应头 Content-Type: application/xml

示例代码 (Spring Boot):

假设你有一个POJO:

// src/main/java/com/example/demo/User.java
package com.example.demo;// 如果使用JAXB,需要添加 @XmlRootElement
// import javax.xml.bind.annotation.XmlRootElement;
// @XmlRootElement
public class User {private Long id;private String name;private String email;// 构造函数、Getters 和 Setterspublic User(Long id, String name, String email) {this.id = id;this.name = name;this.email = email;}public Long getId() { return id; }public void setId(Long id) { this.id = id; }public String getName() { return name; }public void setName(String name) { this.name = name; }public String getEmail() { return email; }public void setEmail(String email) { this.email = email; }
}

Controller:

// src/main/java/com/example/demo/UserController.java
package com.example.demo;import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {@GetMapping(value = "/users/{id}", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })public User getUser(@PathVariable Long id) {// 实际应用中会从服务层或数据库获取用户return new User(id, "John Doe " + id, "john.doe" + id + "@example.com");}
}

依赖:

  • 对于JSON: Spring Boot 的 spring-boot-starter-web 默认包含了 Jackson,所以通常不需要额外添加。
  • 对于XML: 你需要添加相应的XML处理库。
    • 使用Jackson XML:
      <!-- pom.xml -->
      <dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId>
      </dependency>
      
      如果使用Jackson XML,它会自动注册 MappingJackson2XmlHttpMessageConverter
    • 使用JAXB:
      从Java 9开始,JAXB不再是JDK的一部分,需要手动添加依赖:
      <!-- pom.xml -->
      <dependency><groupId>jakarta.xml.bind</groupId><artifactId>jakarta.xml.bind-api</artifactId><!-- Version may vary -->
      </dependency>
      <dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><!-- Version may vary -->
      </dependency>
      
      并且在你的POJO上使用 @XmlRootElement (以及其他JAXB注解,如 @XmlElement)。

如何测试:

使用 curl 或 Postman:

  1. 请求JSON:

    curl -H "Accept: application/json" http://localhost:8080/users/1
    

    响应头会包含 Content-Type: application/json,响应体是JSON格式的用户数据。

  2. 请求XML:

    curl -H "Accept: application/xml" http://localhost:8080/users/1
    

    响应头会包含 Content-Type: application/xml,响应体是XML格式的用户数据。

produces 属性:

@RequestMapping (或 @GetMapping 等) 中使用 produces 属性,例如 produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE },是一个好习惯。它明确声明了这个端点能够生成哪些媒体类型。如果客户端请求的 Accept 类型与 produces 中声明的任何一种都不匹配,Spring MVC 通常会返回 406 Not Acceptable 状态码。

自定义内容协商策略 (可选):

如果想更精细地控制内容协商的行为(例如,禁用路径扩展名策略,或设置默认的内容类型),可以通过实现 WebMvcConfigurer 接口并重写 configureContentNegotiation 方法来进行配置。

// import org.springframework.context.annotation.Configuration;
// import org.springframework.http.MediaType;
// import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
// import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;// @Configuration
// public class WebConfig implements WebMvcConfigurer {
//
//     @Override
//     public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
//         configurer.favorParameter(false) // 不使用URL参数 (?format=json)
//                   .favorPathExtension(false) // 不使用路径扩展 (.json)
//                   .ignoreAcceptHeader(false) // 优先使用Accept头 (默认)
//                   .defaultContentType(MediaType.APPLICATION_JSON) // 默认返回JSON
//                   .mediaType("json", MediaType.APPLICATION_JSON)
//                   .mediaType("xml", MediaType.APPLICATION_XML);
//     }
// }

通过这种方式,Spring MVC 提供了一个强大且灵活的机制,使RESTful API能够根据客户端的需求返回不同格式的数据,而Controller层的代码保持简洁,不需要关心具体的序列化细节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/254.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java 线程的堆栈跟踪信息

Java 线程的堆栈跟踪信息&#xff0c;展示了线程的当前状态和执行位置。以下是详细解释&#xff1a; 线程基本信息 "Thread-0" #16 prio5 os_prio0 cpu0.00ms elapsed16.29s tid0x00000243105a4130 nid0x5384 waiting on condition [0x0000007687ffe000]线程名称…

机器学习管道 pipeline

知识回顾: 转化器和估计器的概念管道工程ColumnTransformer和Pipeline类 作业&#xff1a; 整理下全部逻辑的先后顺序&#xff0c;看看能不能制作出适合所有机器学习的通用pipeline 基础概念 pipeline在机器学习领域可以翻译为“管道”&#xff0c;也可以翻译为“流水线”&…

硬件工程师笔记——电子器件汇总大全

目录 1、电阻 工作原理 欧姆定律 电阻的物理本质 一、限制电流 二、分压作用 三、消耗电能&#xff08;将电能转化为热能&#xff09; 2、压敏电阻 伏安特性 1. 过压保护 2. 电压调节 3. 浪涌吸收 4. 消噪与消火花 5. 高频应用 3、电容 工作原理 &#xff08;…

【React中useRef钩子详解】

一、useRef的核心特性 useRef是React提供的Hook,用于在函数组件中创建可变的持久化引用,具有以下核心特性: 持久化存储 返回的ref对象在组件整个生命周期内保持不变,即使组件重新渲染,current属性的值也不会丢失。无触发渲染 修改ref.current的值不会导致组件重新渲染,适…

在 C++中,指针数组与数组指针的区别

1. 指针数组:本质上是一个数组,数组中的每个元素都是一个指针。也就是说,这个数组存储的是多个指针变量,这些指针可以指向不同的对象(比如不同的变量、数组等) 。 2. 数组指针:本质上是一个指针,这个指针指向一个数组。即它指向的是数组的首地址,通过这个指针可以操作…

python打卡训练营打卡记录day22

复习日 仔细回顾一下之前21天的内容&#xff0c;没跟上进度的同学补一下进度。 作业&#xff1a; 自行学习参考如何使用kaggle平台&#xff0c;写下使用注意点&#xff0c;并对下述比赛提交代码 kaggle泰坦尼克号人员生还预测 导入数据 # 导入所需库 import pandas as pd impor…

Python并发编程:开启性能优化的大门(7/10)

1.引言 在当今数字化时代&#xff0c;Python 已成为编程领域中一颗璀璨的明星&#xff0c;占据着编程语言排行榜的榜首。无论是数据科学、人工智能&#xff0c;还是 Web 开发、自动化脚本编写&#xff0c;Python 都以其简洁的语法、丰富的库和强大的功能&#xff0c;赢得了广大…

支持向量机与逻辑回归的区别及 SVM 在图像分类中的应用

支持向量机与逻辑回归的区别及 SVM 在图像分类中的应用 在机器学习的多元算法领域中&#xff0c;支持向量机&#xff08;SVM&#xff09;和逻辑回归&#xff08;LR&#xff09;作为两种经典的监督学习算法&#xff0c;被广泛应用于各类分类任务。尽管它们有着相似的目标&#…

powerbuilder9.0中文版

经常 用这个版本号写小软件,非常喜欢这个开发软件 . powerbuilder9.0 非常的小巧,快捷,功能强大,使用方便. 我今天用软件 自己汉化了一遍&#xff0c;一些常用的界面都已经翻译成中文。 我自己用的&#xff0c;以后有什么界面需要翻译&#xff0c;再更新一下。 放在这里留个…

Qt中在子线程中刷新UI的方法

Qt中在子线程中刷新UI的方法 在Qt中UI界面并不是线程安全的&#xff0c;意味着在子线程中不能随意操作UI界面组件&#xff08;比如按钮、标签&#xff09;等&#xff0c;如果强行操作这些组件有可能会导致程序崩溃。那么在Qt中如何在子线程中刷新UI控件呢&#xff1f; 两种方…

PXE_Kickstart_无人值守自动化安装系统

文章目录 1. PXE2. 配置服务参数2.1 tftp服务配置2.2 dhcp服务配置2.3 http服务配置 3. 配置PXE环境3.1 网络引导文件pxelinux.03.2 挂载镜像文件3.3 创建配置文件default3.4 复制镜像文件和驱动文件3.5 修改default文件3.6 配置ks.cfg文件 4. PXE客户端4.1 创建虚拟机&#xf…

一、数据仓库基石:核心理论、分层艺术与 ETL/ELT 之辨

随着企业数据的爆炸式增长&#xff0c;如何有效地存储、管理和分析这些数据&#xff0c;从中提炼价值&#xff0c;成为现代企业的核心竞争力之一。数据仓库 (Data Warehouse, DW) 正是为此而生的关键技术。理解其基础理论对于构建高效的数据驱动决策体系至关重要。 一、数据库…