欢迎光临
我们一直在努力

同一个接口,浏览器返回 XML、Apifox 返回 JSON?一次 Spring Boot 内容协商排查记录

同一个接口,浏览器返回 XML、Apifox 返回 JSON?一次 Spring Boot 内容协商排查记录

问题现象

项目里有一个再普通不过的登录接口:

@RestController
@RequestMapping("/user")
public class UserController {

@GetMapping("/login")
public Result<User> login(@RequestParam(defaultValue = "guest") String username,
@RequestParam(defaultValue = "123456") String password) {
if (username == null || username.trim().isEmpty()) {
return Result.error(ResultCode.BAD_REQUEST, "用户名不能为空");
}
return Result.success(new User(username, password));
}
}

同一个接口、同一份代码,却出现了诡异的现象:

  • 用 Apifox / Postman 请求,返回的是 JSON
  • 用 浏览器地址栏直接访问,返回的却是 XML

代码里既没写 produces,也没做任何格式相关处理,为什么会这样?

根本原因:Spring MVC 的内容协商机制

答案在于 Spring MVC 的 内容协商(Content Negotiation)。

Spring 决定用什么格式序列化响应,默认会参考请求头里的 Accept。而不同客户端发送的 Accept 是不一样的:

客户端典型 Accept 头结果
Apifox / Postman Accept: */* 无明确偏好,走默认 JSON
浏览器 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 明确带 application/xml,优先级高于 */*

关键就在浏览器这一行。它带的 application/xml;q=0.9 权重(0.9)高于 */*;q=0.8(0.8)。当 classpath 上恰好存在能处理 XML 的 HttpMessageConverter 时,Spring 就"投其所好",返回了 XML。

Apifox 只发 */*,没有明确偏好,Spring 就用它默认的 Jackson JSON 转换器,所以一直是 JSON。

同样的接口,因为请求头不同,走了不同的消息转换器。

定位:XML 转换器是从哪冒出来的?

那么问题变成了:我并没有主动引入 XML 支持,classpath 上怎么会有 XML 转换器?

Spring Boot 有个自动配置逻辑——只要检测到 jackson-dataformat-xml 在 classpath 上,就会自动注册 MappingJackson2XmlHttpMessageConverter。

于是用 dependency:tree 排查依赖树:

./mvnw dependency:tree | grep -E "dataformat-xml|woodstox|stax2"

结果发现了它们:

com.fasterxml.jackson.dataformat:jackson-dataformat-xml:jar:2.21.4:compile
├─ org.codehaus.woodstox:stax2-api:jar:4.2.2:compile
└─ com.fasterxml.woodstox:woodstox-core:jar:7.1.1:compile

继续追它的来源:

./mvnw dependency:tree | grep -E "com.aliyun|hutool|dataformat-xml"

+- com.aliyun:alibabacloud-oss-v2:jar:0.5.0:compile
| +- com.fasterxml.jackson.dataformat:jackson-dataformat-xml:jar:2.21.4:compile
\\- cn.hutool:hutool-all:jar:5.8.47:compile

真相大白:jackson-dataformat-xml 是被阿里云 OSS SDK com.aliyun:alibabacloud-oss-v2 传递引入的,我自己从来没直接依赖过它。就是这个"隐形"的传递依赖,触发了 Spring 的 XML 自动配置。

解决方案

针对这个问题,有三种思路,从"锁单个接口"到"全局兜底"再到"彻底根治":

方案一:给单个接口写死 produces(临时)

@GetMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE)

只影响这一个接口,其它接口浏览器访问依旧返回 XML。适合临时救急,不适合全局。

方案二:配置内容协商,强制默认 JSON(全局兜底)

新建一个 WebConfig,把默认响应类型设为 JSON,并关闭基于 URL 参数的协商:

package com.example.springboot4.config;

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
.defaultContentType(MediaType.APPLICATION_JSON)
.favorParameter(false);
}
}

这个配置的核心是设置了 默认类型,把 JSON 在协商链里提到更优先的位置,浏览器那种 application/xml;q=0.9 就不再优先命中 XML。

如果想在配置文件里做,也可以:

spring:
mvc:
contentnegotiation:
favor-parameter: false

方案三:排除多余的 XML 依赖(治本)

既然 XML 转换器是因为 jackson-dataformat-xml 存在才被注册的,那把这个传递依赖排除掉,classpath 上没有 XML converter,Spring 就压根没法返回 XML:

<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibabacloud-oss-v2</artifactId>
<version>0.5.0</version>
<scope>compile</scope>
<exclusions>
<!– 排除会触发 Spring 内容协商返回 XML 的传递依赖 –>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>

排除后再验证一次依赖树,确认干净了:

./mvnw dependency:tree | grep -E "dataformat-xml|woodstox|stax2"
# 无任何输出,说明已彻底移除

注意:阿里云 OSS v2 SDK 内部解析 XML 响应用的是它自己的逻辑,通常不依赖这个 Jackson XML 模块。排除后建议跑一下实际的 OSS 上传/下载功能验证。如果报 ClassNotFoundException 之类的错,就去掉 <exclusions>,只保留方案二的 WebConfig 兜底即可。

最终采用的方案

我采用了 方案二 + 方案三双保险:

  • 方案三(治本)——在 pom.xml 中排除 alibabacloud-oss-v2 传递引入的 jackson-dataformat-xml。classpath 上再无 XML 转换器。
  • 方案二(兜底)——WebConfig 把默认响应类型设为 JSON,即便未来又有别的依赖带进 XML 支持,也能兜住。
  • 两层防护到位后,编译通过,浏览器访问 /user/login 稳定返回 JSON,问题解决。

    赞(0)
    未经允许不得转载:171主机测评 » 同一个接口,浏览器返回 XML、Apifox 返回 JSON?一次 Spring Boot 内容协商排查记录
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址