Spirng Boot 返回:415 Unsupported MediaType

1、概览

本文将带你了解如何解决在 Spring Boot API 返回状态码 “415 Unsupported MediaType” 的问题,以及出现该问题的原因。

2、背景

我们一个老客户要求我们给他的系统再开发一个桌面应用,类似于用户管理。新的桌面应用,直接调用原系统的 API 服务。

3、API 请求

通过 API 来获取所有用户:

curl -X GET https://baeldung.service.com/user

成功获取响应。接着,获取一个单独的用户:

curl -X GET https://baeldung.service.com/user/{user-id}

响应如下:

{
    "id": 1,
    "name": "Jason",
    "age": 23,
    "address": "14th Street"
}

一切也OK,根据响应,可以确定用户拥有以下参数:idnameageaddress

现在,尝试添加新用户:

curl -X POST -d '{"name":"Abdullah", "age":28, "address":"Apartment 2201"}' https://baeldung.service.com/user/

结果,这次服务器响应了 HTTP 状态码为 415 的错误响应:

{
    "timestamp": "yyyy-MM-ddThh:mm:ss.SSS+00:00",
    "status": 415,
    "error": "Unsupported Media Type",
    "path": "/user/"
}

在弄清 “为什么会出现这个错误?” 之前,需要先弄清楚 “这个错误是什么?”。

4、状态码 415:Unsupported MediaType

根据 RFC 7231 规范标题 HTTP/1.1 语义和内容的第 6.5.13 节

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

如规范所示,415 错误的原因是,请求的媒体类型(Media Typee)不受 API 的支持。之所以选择 JSON 作为媒体类型,是因为 GET 请求的响应。响应数据格式为 JSON。因此,假设 POST 请求也会接受 JSON。然而,事实证明这一假设是错误的。

为了找到 API 支持的格式,研究了一下服务器端的后台源码,其 API 的定义如下:

@PostMapping(value = "/", consumes = {"application/xml"})
void AddUser(@RequestBody User user)

这就清楚地说明,API 将只支持 XML 格式。有人可能会问: @PostMapping 中的这个 consumes 属性有什么作用?

根据 Spring 文档 所述,consumes 属性的作用是:

缩小了可以被 Handler 消费的媒体类型(Media Type)范围。由一个或多个媒体类型组成,其中之一必须与请求的 Content-Type 头匹配。

5、解决

解决这个问题,有两种方案。第一个方案是根据服务器的期望更改请求 Payload 格式。第二个方案是更新 API,使其开始支持 JSON 格式。

5.1、将请求的 Payload 更改为 XML

第一种方法是以 XML 格式而不是 JSON 格式发送请求:

curl -X POST -d '<user><name>Abdullah</name><age>28</age><address>Apartment 2201</address></user>' https://baeldung.service.com/user/

但这还是会收到与上述请求相同的错误。这是因为,没有给请求添加对应的 Content-Type Header。

添加 Content-Type: application/xml 请求头,表示客户端的 Payload 类型是 XML,这与服务器 @PostMappingconsumes 属性对应。

curl -X POST -H "Content-Type: application/xml" -d '<user><name>Abdullah</name><age>28</age><address>Apartment 2201</address></user>' https://baeldung.service.com/user/

这一次,终于获取到了成功的响应。不过,如果遇到不支持 XML 格式的客户端的话就比较麻烦。最灵活的方式还是修改服务器,以支持各种通用的格式。

5.2、更新服务器 API

有几种方式可以修改 API 支持的媒体类型。

第一个也是最业余的方式是在 API 用 JSON 取代 XML:

@PostMapping(value = "/", consumes = {"application/json"}) 
void AddUser(@RequestBody User user)

测试,客户端发送 JSON 格式的请求:

curl -X POST -H "Content-Type: application/json" -d '{"name":"Abdullah", "age":28, "address":"Apartment 2201"} https://baeldung.service.com/user/'

响应成功。但是现在导致了一个更严重的问题,以前那些以 XML 格式发送请求的客户端全部会返回 “415 Unsupported Media Type” 错误。

第二种也是比较简单的方法是允许在请求 Payload 中使用各种格式:

@PostMapping(value = "/", consumes = {"*/*"}) 
void AddUser(@RequestBody User user

以 JSON 格式请求时,响应是成功的。然而,这里的问题在于它过于灵活,它支持所有格式的请求。这可能会导致整个代码库(客户端和服务器端)的行为不一致。

第三种也是推荐的一种方法是专门添加客户端应用目前正在使用的格式。由于 API 已经支持 XML 格式,并且有客户端在使用,应该留它。为了让 API 也支持新的请求格式,可以对 API 代码进行简单的修改:

@PostMapping(value = "/", consumes = {"application/xml","application/json"}) 
void AddUser(@RequestBody User user

以 JSON 格式发送请求后,响应是成功的。在这种特殊情况下,推荐使用这种方法。

6、总结

本文介绍了 Spring Boot API 接口返回 “415 Unsupported Media Type” 错误的原因,以及解决办法。


参考:https://www.baeldung.com/spring-415-unsupported-mediatype