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

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

问题描述:

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

I have an existing Web API 2 service and need to modify one of the methods to take a custom object as another parameter, currently the method has one parameter which is a simple string coming from the URL. After adding the custom object as a parameter I am now getting a 415 unsupported media type error when calling the service from a .NET windows app. Interestingly, I can successfully call this method using javascript and the jquery ajax method.

Web API 2服务方法如下:

The Web API 2 service method looks like this:

<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

该方法同时允许POSTGET,因为Paging对象是可选的.如果我用GET请求调用此方法,它将起作用.

The method allows both POST and GET because the Paging object is optional. If I call this method with a GET request it works.

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

And the simple .NET client code calling the service looks like this:

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()行.我已经尝试过的一些东西:

The .NET client is running on the 4.5 framework and the service is on 4.5.2 framework. The error is thrown at the resp = req.GetResponse() line. Some things I tried already:

    在客户端上的
  • ,将req.Accept值设置为"application/xml"或 文本/xml"
  • 在服务方法中,删除了该行 `resp.Content.Headers.ContentType.MediaType ="text/xml"
  • 用一些静态JSON替换XML响应内容,试图排除在请求上发送JSON并在响应上获取XML的任何问题
  • on the client, set the req.Accept value to "application/xml" or "text/xml"
  • in the service method, removed the line `resp.Content.Headers.ContentType.MediaType = "text/xml"
  • replace the XML response content with some static JSON, tried to rule out any problems with sending in JSON on the request and getting XML back on the response

到目前为止,无论我尝试什么,我都会不断收到相同的415错误响应.

So far I keep getting the same 415 error response no matter what I try.

我提到从javascript调用时此方法有效,这是有效的ajax调用:

I mentioned this works when called from javascript, here's my ajax call that is working:

$.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 API2.我知道路由正在工作,调用已正确路由到该方法,它们不会意外地移动到其他地方,所以.NET客户端中缺少什么?任何帮助深表感谢!

On the service side, there is nothing fancy going on with the route config or anything else, it's pretty much all out-of-the-box Web API 2. I know the routing is working, calls are being correctly routed to the method, they're not going somewhere else unexpectedly, so what am I missing in the .NET client? Any help is much appreciated!

---更新---
我试图创建一个全新的Web API服务以排除现有服务的任何可能的问题,我创建了一个控制器,该控制器具有一个将自定义对象作为参数的单一方法.然后,我尝试从.NET客户端调用它,并得到相同的错误.我也尝试使用WebClient代替HttpWebRequest,但仍然遇到相同的错误.这也是以前使用Web API(在Web API 2之前)对我有用的东西.

--- UPDATE ---
I tried to create a completely new Web API service to rule out any possible issues with the existing service, I created a controller with a single method that takes a custom object as the parameter. I then tried calling that from the .NET client and got the same error. I also tried using WebClient instead of HttpWebRequest, but still get the same error. This is also something that previously worked for me with Web API (prior to Web API 2).

---更新---
我还尝试使用Web API 1创建一个新的Web应用程序,当我使用POST调用它时,我的复杂对象参数现在为null.我有另一个运行Web API 1的Web服务,并验证了我仍然可以使用复杂对象成功调用该服务.无论我的问题是什么,它似乎都是在客户端和服务器之间传递JSON所致.我已经检查了要发送的JSON及其有效性,对象定义也是客户端和服务器之间的精确匹配,因此服务器应该可以解析JSON.

--- UPDATE ---
I also tried creating a new web app using Web API 1, when I call that with a POST my complex object parameter is now coming in null. I have another web service running Web API 1 and verified that I can still call that successfully with complex objects. Whatever my problem is, it appears to be something with the JSON passing between the client and server. I have checked the JSON I'm sending it and its valid, the object definition is also an exact match between the client and server so the JSON should be able to be parsed by the server.

已解决
在将我的头撞在墙上几天之后,看起来问题似乎与客户端和服务器之间的内容类型协商有关.我使用Fiddler来检查来自客户端应用程序的请求详细信息,对此进行了更深入的研究,这是fiddler捕获的原始请求的屏幕截图:

SOLVED
After banging my head on the wall for a couple days with this issue, it was looking like the problem had something to do with the content type negotiation between the client and server. I dug deeper into that using Fiddler to check the request details coming from the client app, here's a screenshot of the raw request as captured by fiddler:

Content-Type标头显然是缺少的,即使我按照原始文章的代码示例中的设置进行设置.我以为即使设置了Content-Type,也没能通过Content-Type,这很奇怪,所以我又看了我的另一个(工作)代码,该代码调用了另一个Web API服务,唯一的区别是我碰巧正在设置在这种情况下,将req.ContentType属性写入请求主体之前.我对该新代码进行了更改,然后执行了该操作,现在显示了Content-Type,并且从Web服务获得了预期的成功响应.现在,.NET客户端中的新代码如下所示:

What's obviously missing there is the Content-Type header, even though I was setting it as seen in the code sample in my original post. I thought it was strange that the Content-Type never came through even though I was setting it, so I had another look at my other (working) code calling a different Web API service, the only difference was that I happened to be setting the req.ContentType property prior to writing to the request body in that case. I made that change to this new code and that did it, the Content-Type was now showing up and I got the expected success response from the web service. The new code from my .NET client now looks like this:

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属性

That's all it was, the ContentType property just needed to be set prior to writing to the request body

我相信这种行为是因为一旦将内容写入主体,然后将其流式传输到被调用的服务端点,则需要在此之前设置与该请求有关的任何其他属性.如果我输入错误或需要更多详细信息,请纠正我.

I believe this behavior is because once content is written to the body it is streamed to the service endpoint being called, any other attributes pertaining to the request need to be set prior to that. Please correct me if I'm wrong or if this needs more detail.