当调用Web API 2端点时出现HTTP 415不支持的媒体类型错误

68

我有一个现有的Web API 2服务,需要修改其中一个方法以接受自定义对象作为另一个参数,目前该方法只有一个参数,该参数是来自URL的简单字符串。在将自定义对象作为参数添加后,从.NET Windows应用程序调用服务时,现在出现了415不支持的媒体类型错误。有趣的是,我可以使用JavaScript和jQuery ajax方法成功调用此方法。

Web API 2服务方法如下:

<HttpPost>
<HttpGet>
<Route("{view}")>
Public Function GetResultsWithView(view As String, pPaging As Paging) As HttpResponseMessage
   Dim resp As New HttpResponseMessage
   Dim lstrFetchXml As String = String.Empty
   Dim lstrResults As String = String.Empty

   Try
      '... do some work here to generate xml string for the response
      '// write xml results to response
      resp.Content = New StringContent(lstrResults)
      resp.Content.Headers.ContentType.MediaType = "text/xml"
      resp.Headers.Add("Status-Message", "Query executed successfully")
      resp.StatusCode = HttpStatusCode.OK
   Catch ex As Exception
      resp.StatusCode = HttpStatusCode.InternalServerError
      resp.Headers.Add("Status-Message", String.Format("Error while retrieving results from view {0}: {1}", view, ex.Message))
   End Try
   Return resp
End Function

由于 Paging 对象是可选的,所以该方法允许使用 POSTGET。如果我使用 GET 请求调用此方法,它就可以工作。

调用服务的简单 .NET 客户端代码如下:

Dim uri As String = BASE_URI + "fetch/someview"
Dim resp As HttpWebResponse
Dim sr As StreamReader
Dim lstrResponse As String
Dim reqStream As Stream
Dim bytData As Byte()
Dim req As HttpWebRequest = WebRequest.Create(uri)
Dim lstrPagingJSON As String
Dim lPaging As New Paging
Try
   lPaging.Page = 1
   lPaging.Count = 100
   lPaging.PagingCookie = ""
   req.Method = "POST"
   lstrPagingJSON = JsonSerializer(Of Paging)(lPaging)
   bytData = Encoding.UTF8.GetBytes(lstrPagingJSON)
   req.ContentLength = bytData.Length
   reqStream = req.GetRequestStream()
   reqStream.Write(bytData, 0, bytData.Length)
   reqStream.Close()
   req.ContentType = "application/json"

   resp = req.GetResponse()

   sr = New StreamReader(resp.GetResponseStream, Encoding.UTF8)
   lstrResponse = sr.ReadToEnd
   '// do something with the response here
Catch exweb As WebException
   txtOutput.AppendText("Error during request: " + exweb.Message)
Catch ex As Exception
   txtOutput.AppendText(String.Format("General error during request to {0}: {1}", uri, ex.Message))
End Try

.NET客户端运行在4.5框架上,服务运行在4.5.2框架上。错误出现在resp = req.GetResponse()这一行。我已经尝试过以下方法:

  • 在客户端中将req.Accept的值设置为"application/xml"或"text/xml"
  • 在服务方法中删除了这一行`resp.Content.Headers.ContentType.MediaType = "text/xml"
  • 用一些静态JSON替换了XML响应内容,试图排除发送请求和获取响应时任何与JSON有关的问题

到目前为止,无论我尝试什么,都会得到相同的415错误响应。

我提到了当从Javascript调用时可以工作,这是我的ajax调用,它正在工作:

$.ajax({
   headers: {},
   url: "api/fetch/someview",
   type: "POST",
   data: "{Count:100,Page:1,PagingCookie:\"\"}",
   contentType: "application/json; charset=utf-8",
   dataType: "xml",
   success: function (data) {
      alert("call succeeded");
   },
   failure: function (response) {
      alert("call failed");
   }
});

在服务端,路由配置或其他任何内容都没有什么花哨的东西,它几乎全部使用Web API 2的开箱即用功能。我知道路由正在工作,调用被正确地路由到方法,它们没有意外地去了其他地方,那么在.NET客户端中我错过了什么?非常感谢任何帮助!

--- 更新 ---
我尝试创建一个全新的Web API服务,以排除现有服务可能存在的任何问题,我创建了一个控制器,其中包含一个以自定义对象为参数的单个方法。然后我尝试从.NET客户端调用该方法,并收到了相同的错误。我还尝试使用WebClient而不是HttpWebRequest,但仍然收到相同的错误。这也是之前对于Web API(Web API 2之前)对我有效的一些东西。

--- 更新 ---
我还尝试创建一个使用Web API 1的新Web应用程序,在我使用POST时,我的复杂对象参数现在为空。我有另一个运行Web API 1的Web服务,并验证我仍然可以成功地调用具有复杂对象的服务。无论我的问题是什么,它似乎都与客户端和服务器之间的JSON传递有关。我已经检查了我发送的JSON并且它是有效的,对象定义也是客户端和服务器之间的完全匹配,因此JSON应该能够被服务器解析。


1
当我进行DELETE并添加不需要的JSON有效负载时,出现了415错误。 - kmarsh
7个回答

77

问题已解决
在遇到这个问题之前,我一直在苦苦思索了几天。看起来问题与客户端和服务器之间的内容类型协商有关。为了深入研究,我使用 Fiddler 检查了来自客户端应用程序的请求详细信息,以下是 Fiddler 捕获的原始请求的截图:

Fiddler capture of http request from client app

显而易见的是缺少 Content-Type 标头,尽管我像原始帖子中的代码示例中所示设置了它。我觉得很奇怪,即使我已经设置了 Content-Type,它从未被接受,因此我重新查看了我的其他(工作正常)调用不同 Web API 服务的代码。唯一的区别是在写入请求正文之前,我碰巧在该情况下设置了 req.ContentType 属性。我对这个新代码进行了更改,然后问题就解决了。现在 Content-Type 显示出来了,并且我从 Web 服务获取了预期的成功响应。我 .NET 客户端的新代码如下:

req.Method = "POST"
req.ContentType = "application/json"
lstrPagingJSON = JsonSerializer(Of Paging)(lPaging)
bytData = Encoding.UTF8.GetBytes(lstrPagingJSON)
req.ContentLength = bytData.Length
reqStream = req.GetRequestStream()
reqStream.Write(bytData, 0, bytData.Length)
reqStream.Close()
'// Content-Type was being set here, causing the problem
'req.ContentType = "application/json"

就是这样,只需在写入请求正文之前设置 ContentType 属性即可。

我认为这种行为是因为一旦内容被写入正文中,它会被流式传输到正在调用的服务端点,任何与请求相关的其他属性都需要在此之前设置。 如果我理解有误或者需要更多细节,请纠正我。


谢谢,这对我也有帮助。如果您使用fetch(),则可以按照以下方式完成:const options = { method: 'POST', headers: new Headers({ "content-type": "application/json" }), body: JSON.stringify(body), }; await fetch(url, options); - Peter Widmer

50

当我调用我的Web API端点时,我遇到了这个问题并解决了它。

在我的情况下,问题出在客户端对请求正文内容进行编码的方式上。我没有指定编码或媒体类型。指定它们解决了这个问题。

没有指定编码类型 导致了415错误:

var content = new StringContent(postData);
httpClient.PostAsync(uri, content);

指定编码和媒体类型,成功:

var content = new StringContent(postData, Encoding.UTF8, "application/json");
httpClient.PostAsync(uri, content);

2
在.NET Core库中是否有任何常量可以使用魔法字符串"application/json" - CSJ
1
@CSJ 在 System.Net.Mime 命名空间中有一些(例如 System.Net.Mime.MediaTypeNames.Application.pdf 给出了 "application/pdf"),但列表并不完整,对于我们这里的特定目的,也没有一个用于 "application/json" 的常量,也不是核心/标准库的一部分。请参见 https://dev59.com/2Gkv5IYBdhLWcg3wwjkI - 有人非常友好地创建了一个更完整的常量列表。 - monty
MediaTypeNames.Application.Json - undefined

12

我也遇到了这个错误。

我在头部添加了Content-Typeapplication/json。做出这个改变后,我的提交成功了!


3
一个看起来像评论的答案需要编辑,不建议使用。 - Ṃųỻịgǻňạcểơửṩ
很好。完整的端到端示例在这里:https://dev59.com/bFgR5IYBdhLWcg3wyv9c#52003230 - Manohar Reddy Poreddy
1
@Mulliganaceous - 我使用的是"高级 REST 客户端",这个答案虽然简洁,但对我很有帮助。我添加了头部"Content-Type"和值"application/json",重新提交了请求,然后它就起作用了。请注意,内容类型可能需要与控制器的"Produces"属性中指定的匹配(例如 [Produces("application/json")])。 - CJBS

8
我试图编写一段既能在Mac上运行也能在Windows上运行的代码。尽管代码在Windows上运行良好,但在Mac上却提示'不支持的媒体类型'。以下是我使用的代码,其中下面一行使代码在Mac上也能正常运行:
Request.AddHeader "Content-Type", "application/json"

这是我的代码片段:

这里有我的代码片段:

Dim Client As New WebClient
Dim Request As New WebRequest
Dim Response As WebResponse
Dim Distance As String

Client.BaseUrl = "http://1.1.1.1:8080/config"
Request.AddHeader "Content-Type", "application/json" *** The line that made the code work on mac

Set Response = Client.Execute(Request)

3

在我的情况下,这是Asp.Net Core 3.1 API。我将HTTP GET方法从public ActionResult GetValidationRulesForField( GetValidationRulesForFieldDto getValidationRulesForFieldDto)更改为public ActionResult GetValidationRulesForField([FromQuery] GetValidationRulesForFieldDto getValidationRulesForFieldDto),现在它可以工作了。


添加 [FromQuery] 对我也解决了问题。我错过了 MSDN Model Binding docs 中的这个重要声明:“仅对简单类型使用路由数据和查询字符串值。”因此,对于查询字符串中的复杂类型参数,您必须明确告诉它使用 [FromQuery] 进行绑定。 - Bill Menees

0

我在执行需要请求体的DELETE操作时未能发送请求体,结果得到了这个错误信息。


0

以防万一有人遇到了我这样的傻问题:我收到了这个错误,因为我使用了一个用于请求的类/合同,并且作为 HttpGet,我忘记添加 [FromQuery]。

public async Task<IActionResult> EndpointAction([FromQuery] TheContract request)
{
   return Ok();
}

 // The Contract Type
public class TheContract 
{
   public int Id;
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接