74

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.

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

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

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

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:

  • 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

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

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");
   }
});

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?

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).

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.

1
  • 1
    I have gotten this 415 error when doing a DELETE and adding a JSON payload where none was needed. Commented Jul 26, 2017 at 12:36

6 Answers 6

85

The problem had something to do with the content type negotiation between the client and server. I dug deeper using Fiddler to check the request details coming from the client app; here's a screenshot of the raw request as captured by Fiddler:

Fiddler capture of HTTP request from client app

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"

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, that helped me too. In case you use fetch(), the it's done like this: const options = { method: 'POST', headers: new Headers({ "content-type": "application/json" }), body: JSON.stringify(body), }; await fetch(url, options);
50

I experienced this issue when calling my web api endpoint and solved it.

In my case it was an issue in the way the client was encoding the body content. I was not specifying the encoding or media type. Specifying them solved it.

Not specifying encoding type, caused 415 error:

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

Specifying the encoding and media type, success:

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

3 Comments

Are the magic strings "application/json" available as constants anywhere in a .NET Core library?
@CSJ there are some in the System.Net.Mime namespace (e.g. System.Net.Mime.MediaTypeNames.Application.pdf gives "application/pdf") but the list isn't complete and, for our specific purposes here, does not have one for "application/json" nor is part of the core/standard library. See stackoverflow.com/questions/10362140/… - someone has very kindly created a more complete list of constants.
MediaTypeNames.Application.Json
7

I was trying to write a code that would work on both Mac and Windows. The code was working fine on Windows, but was giving the response as 'Unsupported Media Type' on Mac. Here is the code I used and the following line made the code work on Mac as well:

Request.AddHeader "Content-Type", "application/json"

Here is the snippet of my code:

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)

1 Comment

This repeats the solution given by the accepted answer written by OP 3 years prior. Please only add new answers when you have new solutions to add.
3

In my case, in ASP.NET Core 3.1 Web API, needed to add [FromQuery] to my code. I changed the HTTP GET method from

public ActionResult GetValidationRulesForField( GetValidationRulesForFieldDto getValidationRulesForFieldDto)

to

public ActionResult GetValidationRulesForField([FromQuery] GetValidationRulesForFieldDto getValidationRulesForFieldDto)

and it started working.

1 Comment

Adding [FromQuery] fixed the problem for me too. I had missed this important statement from the MSDN Model Binding docs: "Route data and query string values are used only for simple types." So for complex type parameters in a query string, you have to explicitly tell it to bind with [FromQuery].
1

Just in case someone had my silly issue: I received this error because I use a class/contract for requests and being an HttpGet, I forgot to add the [FromQuery].

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

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

1 Comment

My issue was that when calling a GET action I was trying to pass JSON in the body instead of query parameters. It needs the Content-Type: application/json request header. [HttpGet] public async Task<IActionResult> MyAction(MyModel model) { ... } [HttpPost] [ActionName("MyAction")] public async Task<IActionResult> MyAction_Post(MyModel model) { ... } Then I used httpyac to call it: GET {{BaseAddress}}/MyController/MyAction<br>Content-Type: application/json<br><br>{"hello":"world"} Instead of: GET {{BaseAddress}}/MyController/MyAction?hello=world
-1

I was failing to send a body on a DELETE that required one and was getting this message as a result.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.