1. Overview
1.概述
In this tutorial, we’re going to showcase the causes and resolutions of the HTTP response code 415 Unsupported MediaType for POST requests in a Spring Application.
在本教程中,我们将展示Spring应用程序中POST请求的HTTP响应代码415 Unsupported MediaType的原因和解决方案。
2. Backstory
2.背景故事
One of our old business customers has asked us to design and develop a new desktop application for their product. The purpose of this application is to manage the users. We’ve never worked on this product before.
我们的一个老企业客户要求我们为他们的产品设计和开发一个新的桌面应用程序。这个应用程序的目的是为了管理用户。我们以前从未在这个产品上工作过。
Since the timeline is tight, we’ve decided to use their existing set of backend APIs that was written a while ago. The challenge in front of us is that the documentation of these APIs is not very extensive. As a result, we’re only sure about the available API endpoints and their methods. So, we’ve decided not to touch the services – instead, we’ll start working on our application that will be consuming APIs from this service.
由于时间紧迫,我们决定使用他们现有的一套后端API,这套API是在不久前编写的。摆在我们面前的挑战是,这些API的文档并不广泛。因此,我们只能确定可用的API端点及其方法。因此,我们决定不碰这些服务–相反,我们将开始着手开发我们的应用程序,该应用程序将从该服务中消费API。
3. API Requests
3.API请求
Our application has started consuming the APIs. Let’s exercise the API to get all users:
我们的应用程序已经开始消费API。让我们行使API来获取所有的用户。
curl -X GET https://baeldung.service.com/user
Hurrah! The API has responded back with a successful response. After that, let’s request an individual user:
好哇!API已经回馈了一个成功的响应。之后,我们来请求一个单独的用户。
curl -X GET https://baeldung.service.com/user/{user-id}
And let’s check the response:
并让我们检查一下反应。
{
"id": 1,
"name": "Jason",
"age": 23,
"address": "14th Street"
}
This seems to be working as well. Therefore, things are looking smooth. As per the response, we’re able to figure out that the user has the following parameters: id, name, age, and address.
这似乎也在发挥作用。因此,事情看起来很顺利。根据响应,我们能够弄清楚,用户有以下参数。id, name, age, and address.
Now, let’s try to add a new user:
现在,让我们试着添加一个新的用户。
curl -X POST -d '{"name":"Abdullah", "age":28, "address":"Apartment 2201"}' https://baeldung.service.com/user/
As a result, we’ve received an error response with HTTP status 415:
结果,我们收到了一个HTTP状态415的错误响应。
{
"timestamp": "yyyy-MM-ddThh:mm:ss.SSS+00:00",
"status": 415,
"error": "Unsupported Media Type",
"path": "/user/"
}
Before we figure out “Why are we getting this error?”, we need to look into “What is this error?”.
在我们弄清楚 “为什么会出现这个错误?”之前,我们需要研究 “这个错误是什么?”。
4. Status Code 415: Unsupported MediaType
4.状态代码415 不支持的媒体类型
According to the specification RFC 7231 title HTTP/1.1 Semantics and Content section 6.5.13:
根据规范RFC 7231标题HTTP/1.1语义和内容第6.5.13节。
The 415 (Unsupported Media Type) status code indicates that the origin server is refusing to service the request because the payload is in a format not supported by this method on the target resource.
415(不支持的媒体类型)状态代码表示原服务器拒绝为该请求提供服务,因为有效载荷的格式不被目标资源上的这种方法所支持。
As the specification suggests, our chosen media type isn’t supported by the API. The reason for choosing JSON as the media type was because of the response from the GET requests. The response data format was in JSON. Hence, we assumed that the POST request would accept JSON as well. However, that assumption turned out to be wrong.
正如规范所建议的,我们所选择的媒体类型并不被API所支持。选择JSON作为媒体类型的原因是来自GET请求的响应。响应的数据格式是JSON。因此,我们假设POST请求也会接受JSON。然而,这个假设被证明是错误的。
In order to find which format is supported by the API, we’ve decided to dig into the server-side backend code, and we find the API definition:
为了找到API所支持的格式,我们决定挖掘服务器端的后台代码,我们找到了API定义。
@PostMapping(value = "/", consumes = {"application/xml"})
void AddUser(@RequestBody User user)
This explains pretty clearly that the API will only support XML format. One may question here: What’s the purpose of this “consumes” element in Spring?
这很清楚地解释了该API将只支持XML格式。人们可能会在这里提出疑问。这个”consumes“元素在Spring中的作用是什么?
According to the Spring framework documentation, the purpose of the “consumes” element is:
根据Spring框架文档,”consumes“元素的目的是。
Narrows the primary mapping by media types that can be consumed by the mapped handler. Consists of one or more media types one of which must match to the request Content-Type header
通过可以被映射的处理程序消费的媒体类型来缩小主要映射范围。由一个或多个媒体类型组成,其中一个必须与请求的 “内容类型 “头匹配。
5. Resolution
5.决议
There are two options in front of us to resolve the issue. The first option is to change the request payload format according to what the server expects. The second option is to update the API request so that it starts supporting JSON format.
摆在我们面前的有两个选项来解决这个问题。第一个选项是根据服务器的期望,改变请求的有效载荷格式。第二个选项是更新API请求,使其开始支持JSON格式。
5.1. Change Request’s Payload to XML
5.1.将请求的有效载荷改为XML
The first option is to send requests in XML format instead of JSON:
第一个选择是以XML格式而不是JSON发送请求。
curl -X POST -d '<user><name>Abdullah</name><age>28</age><address>Apartment 2201</address></user>' https://baeldung.service.com/user/
Unfortunately, we get the same error as a result of the above request. If we remember, we had asked the question, what is the purpose of that “consumes” element in the API. That points us in the direction that one of our headers (“Content-Type“) is missing. Let’s send the request, this time with the missing header:
不幸的是,我们收到的上述请求的结果是同样的错误。如果我们记得,我们曾问过问题,API中的那个”consumes“元素的目的是什么。这给我们指出了一个方向:我们的一个头信息(”Content-Type“)丢失了。让我们发送请求,这次要把缺失的标头也发送出去。
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/
This time, we received success in the response. However, we may encounter a situation where the client-side application is not able to send the request in the supported format. In that kind of scenario, we have to make changes on the server in order to make things relatively flexible.
这一次,我们在响应中收到了成功。然而,我们可能会遇到这样的情况:客户端的应用程序无法以支持的格式发送请求。在这种情况下,我们必须在服务器上进行修改,以使事情相对灵活。
5.2. Update the API on the Server
5.2.更新服务器上的API
Suppose that our customer has decided to allow us to change the backend services. The second option as mentioned above is to update the API request to start accepting JSON format. There are three further options on how we can update the API request. Let’s explore each, one by one.
假设我们的客户决定允许我们改变后端服务。上面提到的第二个选项是更新API请求,开始接受JSON格式。关于我们如何更新API请求,还有三个选项。让我们逐一探讨。
The first and most amateur option is to replace XML format with JSON on the API:
第一个也是最业余的选择是在API上用JSON取代XML格式。
@PostMapping(value = "/", consumes = {"application/json"})
void AddUser(@RequestBody User user)
Let’s send the request again from our client-side application in JSON format:
让我们再次从我们的客户端应用程序发送JSON格式的请求。
curl -X POST -H "Content-Type: application/json" -d '{"name":"Abdullah", "age":28, "address":"Apartment 2201"} https://baeldung.service.com/user/'
The response will be a success. However, we’ll face a situation where all of our existing clients, who are sending requests in XML format, will now begin getting 415 Unsupported Media Type errors.
响应将是一个成功。然而,我们将面临这样一种情况:我们所有以XML格式发送请求的现有客户,现在将开始收到415个不支持的媒体类型错误。
The second and somewhat easier option is to allow every format in the request payload:
第二种也是比较简单的选择是允许请求有效载荷中的每一种格式。
@PostMapping(value = "/", consumes = {"*/*"})
void AddUser(@RequestBody User user
Upon request in JSON format, the response will be a success. However, the problem here is that it’s too flexible. We don’t want a wide range of formats to be allowed. This will result in inconsistent behavior in the overall codebase (both client-side and server-side).
在以JSON格式请求时,响应将是一个成功。然而,这里的问题是,它太灵活了。我们不希望允许各种各样的格式。这将导致整个代码库(包括客户端和服务器端)中的行为不一致。。
The third and recommended option is to add specifically those formats that the client-side applications are currently using. Since the API already supports the XML format, we’ll keep it there as there are existing client-side applications sending XML to the API. To make the API support our application format as well, we’ll make a simple API code change:
第三种也是推荐的方案是专门添加那些客户端应用程序目前正在使用的格式。由于API已经支持XML格式,我们将保留它,因为现有的客户端应用程序正在向API发送XML。为了使 API 也支持我们的应用程序格式,我们将进行简单的 API 代码修改。
@PostMapping(value = "/", consumes = {"application/xml","application/json"})
void AddUser(@RequestBody User user
Upon sending our request in JSON format, the response will be a success. This is the recommended method in this particular scenario.
在以JSON格式发送我们的请求后,响应将是一个成功。这是在这个特定情况下推荐的方法。
6. Conclusion
6.结语
In this article, we’ve learned that the “Content-Type” header must be sent from the client-side application request in order to avoid the 415 Unsupported Media Type error. Also, the RFC explains clearly that the “Content-Type” header of the client-side application and server-side application must be in sync in order to avoid this error while sending a POST request.
在这篇文章中,我们已经了解到”Content-Type“头必须从客户端应用程序请求中发送,以避免415 Unsupported Media Type错误。另外,RFC清楚地解释了客户端应用程序和服务器端应用程序的”Content-Type“头必须是同步的,以避免在发送POST请求时出现这种错误。
All the code of this article is available over on GitHub.
本文的所有代码都可以在GitHub上找到。