Geonode logo
Geonode Team

Geonode Team

更新于:2026年10月7日

发布于:2026年9月2日

什么是 415 状态码?原因及解决方法

415 错误码表示服务器理解了您的请求,但拒绝了您发送请求主体时使用的格式。这并非因为您的数据有误——而是与封装方式有关。 这就是为什么解决方法几乎总是修改请求头,而不是修改请求体,也是为什么人们在检查 JSON 内容时往往一无所获。 本指南将介绍规范中的实际规定、占绝大多数情况的几种常见原因,以及如何将 415 状态码与常被混淆的其他三个状态码区分开来。

为什么一家代理公司要撰写关于状态码的文章:我们是 Geonode,用户会通过我们转发 API 流量,因此常有人询问某个错误是否由“代理”引起。对于 415 状态码,答案基本上总是“否”。 415 状态码来自源服务器,描述的是您构建的请求中存在的问题。 中间节点在极少数情况下可能会返回此状态码——例如检查请求体的过滤代理,或具有自身内容规则的网关——但这种情况并不常见,且响应头中会明确标注。 如果你通过代理收到 415 错误,请移除代理,几乎可以肯定直接连接时也会收到相同的 415 错误。请修正请求。唯一真正与代理相关的状态码是 407,其名称本身就说明了这一点。

现在,让我们来谈谈实际的错误。

规范中的说明

RFC 9110 第 15.5.16 节对此给出了精确定义:

415(不支持的媒体类型)状态码表示源服务器拒绝处理该请求,因为内容格式不受目标资源上该方法的支持。

该句中的三个部分确实成立。

“内容” —— 指请求正文,而非 URL、查询字符串或响应。如果请求没有正文,返回 415 状态码则较为异常,通常表明存在其他问题。

“该方法不支持” —— 支持情况是按方法决定的。一个资源可能在 POST 请求中接受 application/json,但在 PATCH 请求中拒绝它;当同一个端点在不同动词下表现不同时,这确实是一个常见的混淆来源。

“针对目标资源” ——且按资源而定。API 上某个端口接受某种格式,并不能说明其他端口也接受该格式。

规范随后列出了原因:

格式问题可能是由于请求中指定的 Content-Type 或 Content-Encoding 引起的,也可能是直接检查数据后得出的结果。

最后这一条款至关重要,却常被忽视。服务器在检查字节内容后(而不仅仅是读取请求头)被允许返回 415 状态码。声明 Content-Type: application/json 却发送非 JSON 内容,确实可能导致返回 415 而非 400。

RFC 还规定了服务器应向用户传达的信息。如果问题出在内容编码上,则应使用 Accept-Encoding 响应头“来指示哪些(如有)内容编码会被接受”。如果问题出在媒体类型上,则“Accept 可以用来指示哪些媒体类型会被接受”。 实际上,MDN文档指出,服务器通常会针对特定方法的情况使用 Accept-Post 和 Accept-Patch,这反而更为实用。

请仔细阅读响应头。服务器经常会给出答案,而客户端却常常忽略这些信息。

导致此问题的六大原因

按我们遇到这些问题的频率大致排序。

1. 完全缺少 Content-Type。 你发送了请求体却从未声明其格式。许多框架不会自行推测。MDN 的示例正是如此:一个包含 JSON 请求体的 POST 请求,声明了 Content-Length,但未声明 Content-Type,结果返回了 415 和 Accept-Post: application/json; charset=UTF-8。

2. 错误的媒体类型声明(Content-Type)。 典型的错误是发送 JSON 数据时却声明为 application/x-www-form-urlencoded,这通常是因为 HTTP 客户端默认使用表单编码,而你直接传递了 JSON 字符串却未作任何修改。请求体本身没有问题,只是声明不正确。

3. 接近但错误的媒体类型。 例如使用 text/json 代替 application/json;或者发送 application/xml,而服务器要求的是 text/xml。 供应商特有的媒体类型,例如当你发送纯文本 application/json 时,却使用了 application/vnd.api+json。严格的服务器会进行精确匹配,绝不会网开一面。

4. 字符集问题。 MDN 给出了最典型的例子:当服务器要求 UTF-8 时,却发送了 UTF8。在注册名称中,连字符并非可选,进行严格参数验证的服务器有权拒绝该请求。

5. 服务器不支持的Content-Encoding。 例如,当服务器仅支持身份编码时,你却对请求主体进行 gzip 压缩并设置 Content-Encoding: gzip。RFC 专门预见了这种情况,行为规范的服务器应返回 Accept-Encoding 来告知你它能接受的编码格式。

6. 请求体与声明的类型不匹配。 标头正确,但字节内容错误——这通常是序列化错误、模板输出空字符串,或者请求体在栈的某个环节被重复编码所致。这就是“直接检查数据”条款的实际应用。

415 与 406 与 400 与 422

这是一张混淆对照表,而规范对它们进行了明确区分。

代码含义方向通过修改来修复
415您发送的格式不受支持请求正文Content-Type 或 Content-Encoding
406没有您接受的表示形式可用响应Accept 头部
400请求格式错误整个请求语法或框架
422格式正确,但内容无法处理请求正文数据本身
413正文过大请求正文有效载荷大小

415 与 406 属于方向性问题,一旦明确说明,就最容易理解。 415 涉及的是 你发送的内容。406 涉及的是你请求 接收 的内容——RFC 9110 将其定义为:根据收到的主动协商标头字段,该资源“没有用户代理可接受的当前表示形式”。 若收到 406 响应,请检查你的 Accept 头部,而非请求体。

415 与 400 的区别。 RFC 9110 将 400 描述为服务器“因被视为客户端错误(例如:请求语法格式错误、请求消息框架无效或请求路由欺骗)”而未处理该请求。 400 属于结构性错误——请求本身存在问题。415 则表示请求格式正确,但其正文格式不符合服务器要求。 实际上,许多服务器在应返回 415 时却返回了 400;你无法控制这一点,因此当请求包含正文却收到 400 状态码时,应将其视为可能伪装成的 415。

415 与 422 的区别是 RFC 中阐述得最为明确的。 第 15.5.21 节指出,422 表示“服务器理解请求内容的媒体类型(因此 415(不支持的媒体类型)状态码不适用),且请求内容的语法正确,但无法处理其中包含的指令。”

因此,其逻辑顺序是:415 表示封装格式错误,422 表示封装格式正确但内容有误。格式正确的 JSON 若缺少必填字段,则返回 422;而同样的 JSON 若被标记为表单数据,则返回 415。

两分钟内排查故障

一套固定的步骤,几乎能解决所有问题。

第一步:查看响应头。 注意,不是状态行,而是响应头。

curl -i -X POST https://api.example.com/items \
  -H "Content-Type: application/json" \
  -d '{"name":"test"}'

在响应中查找 Accept、Accept-Post、Accept-Patch 或 Accept-Encoding。如果存在其中任何一个,那就是服务器明确表达的需求,问题即刻解决。

第二步:确认你实际发送的内容。 不是你本意要发送的内容——而是实际通过网络传输的内容。 客户端库会添加、覆盖和重新格式化请求头,因此你在代码中设置的请求头并不总是与实际传输的请求头一致。

curl -v -X POST https://api.example.com/items \
  -H "Content-Type: application/json" \
  -d '{"name":"test"}' 2>&1 | grep '^>'

> 这几行就是你的实际请求。令人惊讶的是,相当一部分 415 错误正是在这里得到解决的——当你精心设置的 Content-Type 被默认值覆盖时,问题便迎刃而解。

**第三步:检查请求方法。**同一个端点在 POST 请求中可能接受某种类型,但在 PATCH 请求中却会拒绝。尝试使用不同的动词发送相同的内容体,观察行为是否发生变化。

**第四步:检查确切的类型字符串。**与文档逐字对比。例如 application/json 与 text/json,UTF-8 与 UTF8。 供应商后缀。这虽然繁琐,但答案往往就在这里。

第五步:阅读该特定端点的文档。 API 在内部并不统一。在一个原本采用 JSON 格式的 API 中,文件上传端点要求使用 multipart/form-data 格式是完全正常的。

在客户端进行修复

常见客户端中的典型情况。

curl。 -d 默认表示 application/x-www-form-urlencoded,除非你特别指定。这是命令行中返回 415 错误的最常见原因:

curl -X POST https://api.example.com/items \
  -H "Content-Type: application/json" \
  -d '{"name":"test"}'

JavaScript fetch。 传递字符串正文时,完全不会设置 Content-Type:

await fetch(url, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "test" }),
});

值得注意的例外情况:使用 FormData 时,请不要自行设置 Content-Type。该字段必须由浏览器自动生成,因为其中包含多部分边界符;若覆盖该值,将导致请求失败,通常表现为 415 错误。

Python requests。 使用 json= 而不是 data=,系统会自动为你设置该头部:

requests.post(url, json={"name": "test"})       # application/json
requests.post(url, data={"name": "test"})       # form-encoded

Axios。 对于普通对象,它会设置 application/json;而对于字符串,则会设置其他值,这常常会让人感到意外。如果你已经对有效载荷进行了序列化,请显式设置该标头。客户端在此处的行为差异,正是我们在 axios 与 fetch 的对比 中讨论过的那类问题。

**一般原则:**当客户端提供专用于 JSON 的参数时,请直接使用该参数,而不是手动序列化并指望默认头部设置正确。

在服务器端正确返回响应

如果你是客户端,以下几点能让你的 API 更易于使用。

在返回 415 状态码时,请发送 Accept-Post 或 Accept-Patch 请求头。 RFC 规定应使用 Accept 或 Accept-Encoding;而针对具体方法的变体更为精确,MDN 也正是为此用途而记录了这些变体。 仅此一个头部,就能将调试会话转化为一目了然的检查。

包含可读的正文。 带有空正文的状态码会迫使客户端进行猜测。请明确说明实际接收的内容以及预期接收的内容。

正确区分 415 和 422。 如果已识别内容类型且正文解析成功,但验证失败,则应返回 422。因验证失败而返回 415 会导致用户去检查头部信息,而实际上他们的头部信息并无问题——这是 API 设计中常见且代价高昂的错误。

在安全允许的情况下,对参数要宽容一些。 当你接受 application/json 却拒绝 application/json; charset=utf-8 时,虽然从技术上讲是站得住脚的,但在实际应用中毫无帮助。请正确解析媒体类型,并忽略你不关心的参数。

不要将 415 作为通用拒绝码使用。 它具有特定的含义。过度使用该状态码会增加 API 的使用难度,并导致客户端重试逻辑出现错误。

当 415 其实并非 415 时

状态码可能造成误导的情况。

**网关或WAF拒绝了请求。**某些安全层会针对其认为可疑的请求体返回415状态码,无论实际内容类型如何。通常的线索是响应体看起来不像来自应用程序,或者头部信息标识了中间件。

在您的代码执行之前,框架的默认处理已被触发。 许多 Web 框架会在中间件中拒绝未知的内容类型。由于您的处理程序从未执行,因此应用程序逻辑中的任何内容都与问题解决无关。

负载均衡器或 CDN 删除了某个标头。 这种情况虽罕见但确实存在。如果请求直接发送时成功,但通过基础设施转发时失败,请在断定是应用程序发生变化之前,先比较两端的标头。

该端点不存在。 某些服务器在处理 POST 请求时,若路由不匹配,会返回 415 状态码而非 404,因为内容类型协商发生在路由解析之前。请检查 URL。

HTTP 方法覆盖出错。 如果您的框架支持通过标头或查询参数覆盖方法,实际生效的方法可能并非您发送的方法,且内容类型的支持是按方法区分的。

在上述所有情况下,解决方法都在请求正文的上游。一般原则是:如果请求显然正确但仍持续收到 415 错误,请停止修改请求正文,转而排查路径中的哪个组件产生了该响应。

大家还问

415“不支持的媒体类型”是什么意思?

服务器拒绝了该请求,因为请求正文的格式不支持该资源上的该方法。RFC 9110 将其归因于 Content-Type 头、Content-Encoding 头,或者服务器直接检查了请求正文。 这涉及您发送内容的格式问题,而非数据本身的正确性。

如何修复 415 错误?

首先检查响应头——服务器通常会返回 Accept、Accept-Post 或 Accept-Encoding 等响应头,明确指定其所需格式。然后验证客户端实际传输的内容,因为库函数可能会覆盖响应头。 最常见的单一解决方法是在原本没有 Content-Type: application/json 的请求中添加该字段。

415 与 400 有什么区别?

400 表示请求格式错误——语法或结构有问题。415 表示请求格式正确,但请求体采用的格式不受支持。 实际上,服务器往往会在本应返回 415 的情况下返回 400,因此对于带有请求体的请求,若收到 400 响应,则值得调查是否存在内容类型问题。

415 和 422 有什么区别?

RFC 9110 对此进行了明确区分:422 表示服务器理解了内容类型且语法正确,但无法处理其中的指令。因此,415 表示封装错误;422 表示内容错误。有效的 JSON 缺少必填字段时,会返回 422。

为什么上传文件时会出现 415 错误?

通常是因为在多部分请求中手动设置了 Content-Type 头部。浏览器或客户端必须自行生成该头部,因为其中包含多部分请求的分界符。手动设置会移除该分界符,从而产生服务器无法解析的请求。

代理服务器会导致 415 错误吗?

很少。415 状态码来自源服务器,描述的是您的请求正文。过滤型代理或检查内容的网关可能会返回此状态码,但通常代理特有的状态码是 407,其名称中已明确说明这一点。请尝试绕过代理进行测试——如果 415 状态码仍然存在,则说明代理并非问题所在。

415 是否意味着我的 JSON 无效?

不一定。如果服务器拒绝了内容类型,说明你的 JSON 根本未被检查。如果服务器声明了正确的类型,随后发现请求体实际上并非该格式,那么是的——RFC 允许在“直接检查数据”后予以拒绝。 请先检查关于请求头的疑问;这种情况更为常见。

遇到 415 错误后应该重试吗?

不应该。这是客户端错误,相同的请求会产生相同的结果。重试会浪费请求,如果受到速率限制,还可能使情况恶化。修正内容类型后发送一次即可。

总结

415 状态码的含义狭窄而精确:表示包裹您数据的封装格式并非该端点对此方法所接受的格式。这既不表示您的数据无效(这属于 422 状态码的范畴),也不涉及您请求接收的内容(这属于 406 状态码的范畴)。

正因含义狭窄,排查过程也较为简短。请仔细阅读响应头,因为行为规范的服务器会在 Accept、Accept-Post 或 Accept-Encoding 中列出可接受的类型。然后,请验证客户端实际发送的内容,而非您指示其发送的内容,因为默认设置和中间件通常会覆盖您设置的头部。 通过这两个步骤,您通常无需触及请求主体即可解决大多数情况。

如果请求看起来无可挑剔,但 415 错误仍然存在,那么响应很可能并非来自您认为的那个应用程序。 网关、框架中间件和路由不匹配都会产生与有效载荷无关的 415 错误——此时有意义的问题不是“该修改什么”,而是“路径中的哪个组件正在响应”。