Spring RestController如何实现URL后缀驱动的XML/JSON响应切换
解决Spring RestController基于URL后缀的内容协商问题
问题分析
你遇到的404错误核心原因是:Spring MVC默认会将URL中的后缀(如.json)视为请求映射的一部分,而非内容协商的标识。当访问/employees.json时,Spring会寻找映射路径为/employees.json的Controller方法,但你的Controller只有/employees的映射,因此返回404。之前的WebMvcConfigurer配置仅设置了内容协商规则,但未配置路径匹配策略,导致后缀无法被正确解析为媒体类型;拦截器方案无效则是因为请求未匹配到Controller就已返回404,拦截器根本不会执行。
解决方案
要复现JAX-RS的URL后缀内容协商功能,需完成以下三步:
1. 添加XML序列化依赖
Spring默认自带JSON序列化支持,但XML需要额外引入Jackson XML依赖(Maven示例):
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
2. 配置WebMvcConfigurer
调整配置,同时设置内容协商规则和路径匹配策略,让Spring将URL后缀解析为媒体类型而非映射路径的一部分:
package com.example.spring_rest_to_graphql; 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.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.util.pattern.PathPatternParser; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.favorPathExtension(true) .favorParameter(false) .ignoreAcceptHeader(true) .useRegisteredExtensionsOnly(true) .defaultContentType(MediaType.APPLICATION_XML) // 与原JAX-RS默认返回XML一致 .mediaType("json", MediaType.APPLICATION_JSON) .mediaType("xml", MediaType.APPLICATION_XML); } @Override public void configurePathMatching(PathMatchConfigurer configurer) { // Spring Boot 2.6+ 默认使用PathPatternParser,开启注册后缀匹配 PathPatternParser parser = new PathPatternParser(); parser.setUseRegisteredSuffixPatternMatch(true); configurer.setPatternParser(parser); // 若使用Spring Boot 2.6之前版本(默认AntPathMatcher),替换为以下代码: // configurer.setUseSuffixPatternMatch(true) // .setUseRegisteredSuffixPatternMatch(true); } }
3. 编写RestController与实体类
确保实体类支持XML序列化,Controller映射路径与原JAX-RS一致:
实体类示例
package com.example.spring_rest_to_graphql; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; @JacksonXmlRootElement(localName = "employee") public class Employee { private int id; private String name; // 必须提供无参构造函数,用于序列化/反序列化 public Employee() {} public Employee(int id, String name) { this.id = id; this.name = name; } // Getter和Setter方法 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Controller示例
package com.example.spring_rest_to_graphql; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Arrays; import java.util.List; @RestController @RequestMapping("/employees") public class EmployeeController { @GetMapping public List<Employee> getEmployees() { return Arrays.asList( new Employee(1, "Alice"), new Employee(2, "Bob") ); } }
验证结果
- 访问
http://localhost:8080/employees:默认返回XML格式 - 访问
http://localhost:8080/employees.json:返回JSON格式 - 访问
http://localhost:8080/employees.xml:返回XML格式
内容的提问来源于stack exchange,提问作者sravan ganji
相关产品推荐
相关产品推荐

