欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > Spring MVC 根据请求头 (如 Accept) 怎么返回 JSON 或 XML 数据?

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

2025/5/13 23:32:34 来源:https://blog.csdn.net/nmsoftklb/article/details/147892465  浏览:    关键词: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层的代码保持简洁,不需要关心具体的序列化细节。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词