338

Sending a form POST HTTP request (Content-Type: application/x-www-form-urlencoded) to the below controller results into a HTTP 415 Unsupported Media Type response.

public class MyController : Controller
{
    [HttpPost]
    public async Task<IActionResult> Submit([FromBody] MyModel model)
    {
        //...
    }
}

Form post HTTP headers:

POST /submit HTTP/1.1
Host: example.com:1337
Connection: keep-alive
Content-Length: 219
Pragma: no-cache
Cache-Control: no-cache
Origin: https://example.com:1337
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: https://example.com:1337/submit
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8,nl;q=0.6

This used to work with ASP.NET MVC 5 on .NET 4.6.

Bart Verkoeijen
  • 16,545
  • 7
  • 52
  • 56

16 Answers16

541

For forms, use the [FromForm] attribute instead of the [FromBody] attribute.

The below controller works with ASP.NET Core 1.1:

public class MyController : Controller
{
    [HttpPost]
    public async Task<IActionResult> Submit([FromForm] MyModel model)
    {
        //...
    }
}

Note: [FromXxx] is required if your controller is annotated with [ApiController]. For normal view controllers it can be omitted.

Austin Hanson
  • 21,820
  • 6
  • 35
  • 41
Bart Verkoeijen
  • 16,545
  • 7
  • 52
  • 56
187

You can use [FromBody] but you need to set the Content-Type header of your request to application/json, i.e.

Content-Type: application/json
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Bjorn Bailleul
  • 3,055
  • 1
  • 20
  • 25
  • 1
    That's why the question specifically mentions a *form* POST, with content-type `application/x-www-form-urlencoded`. Like from a `
    ` on a HTML page.
    – Bart Verkoeijen Sep 18 '17 at 21:36
  • 7
    This was helpful for me, because I was submitting an object, not a form. The accepted answer is the most correct for the OP, who was already using the correct content type for [FromForm]. Still, I'm glad this one was here too. :) – Ken Lyon Jun 26 '18 at 15:42
  • 3
    This does not answer the question at all. Question is how to make server support form bodies, not how to just tell all your clients to stop sending them! – csauve Dec 19 '18 at 22:40
  • 7
    Wait, does that mean it's impossible to ingest content from a request's body different than `application/json`, like `application/text`? @BartVerkoeijen any ideas? – SpiritBob Jun 09 '20 at 11:40
19

First you need to specify in the Headers the Content-Type, for example, it can be application/json.

If you set application/json content type, then you need to send a json.

So in the body of your request you will send not form-data, not x-www-for-urlencoded but a raw json, for example {"Username": "user", "Password": "pass"}

You can adapt the example to various content types, including what you want to send.

You can use a tool like Postman or curl to play with this.

Gabriel P.
  • 3,400
  • 2
  • 32
  • 23
13

As addition of good answers, You don't have to use [FromForm] to get form data in controller. Framework automatically convert form data to model as you wish. You can implement like following.

[HttpPost]
public async Task<IActionResult> Submit(MyModel model)
{
    //...
}
Bernard Vander Beken
  • 4,848
  • 5
  • 54
  • 76
hasan
  • 3,484
  • 1
  • 16
  • 23
  • 5
    Not what I see. – François Oct 03 '18 at 15:27
  • I have tested it and it was working, There can be another problem with your code – hasan Oct 04 '18 at 14:13
  • This solved my problem. I was ajaxing a FormData object with fields and files in it, [FromForm] or [FromBody] did not work. Removed them and it worked. (Asp.Net MVC Core 2.1 back, vanilla js front). [Gist Here](https://gist.github.com/danielszabo1981/5616d25a115ce8dd7cbe5f924f6dd403). – Daniel Szabo Oct 12 '18 at 17:54
  • Funny, a few months after my previous comment -- today I had the same problem in AspNetCore 2.2 Web Api project, and I had to use [FromFrom] to get it working in a WebAPI controller (see @Bart's answer). – Daniel Szabo Jan 19 '19 at 18:18
  • 1
    For me I had a `[FromQuery]` parameter, but I wasn't specifying the Content-Type as `application/json` - adding that in my request, made this work with the [FromQuery] parameter too. – Mike Upjohn Jul 15 '19 at 14:51
  • This seems to work only for requests in the format application/json. For request containing form-data, only using [FromForm] worked for me – nkaenzig Sep 28 '19 at 19:34
  • 1
    I've updated the accepted answer but both @hasan and @François are correct. The `[FromForm]` attribute is only required if the Controller is an `ApiController`. – Austin Hanson Feb 07 '20 at 15:22
9

In my case 415 Unsupported Media Types was received since I used new FormData() and sent it with axios.post(...) but did not set headers: {content-type: 'multipart/form-data'}. I also had to do the same on the server side:

[Consumes("multipart/form-data")]
public async Task<IActionResult> FileUpload([FromForm] IFormFile formFile) { ... }
Vitaly Leskiv
  • 363
  • 3
  • 11
  • It does not work at this time, I still have a 415 with the `[Consumes("multipart/form-data")]`, without I have null for every parameters :( Why is this so hard to do such a basic thing .. – Moff452 Mar 04 '21 at 08:09
  • 1
    @moff452 with multipart you have to use custom model binders(if you use custom model), otherwise you'll end up having nulls. – Vitaly Leskiv Mar 04 '21 at 10:27
  • 1
    Finally, I didnt need decorators or [FromForm], I just forgot to add {get;set;} to each properties ... Now it works like a charm :) – Moff452 Mar 04 '21 at 12:34
8

This is my case: it's run Environment: AspNet Core 2.1 Controller:

public class MyController
{
    // ...

    [HttpPost]
    public ViewResult Search([FromForm]MySearchModel searchModel)
    {
        // ...
        return View("Index", viewmodel);
    }
}

View:

<form method="post" asp-controller="MyController" asp-action="Search">
    <input name="MySearchModelProperty" id="MySearchModelProperty" />
    <input type="submit" value="Search" />
</form>
Quang Vu
  • 362
  • 3
  • 4
7

With .NET 5 I have a .NET API Controller method that looks like this:

[HttpPost("{rootEntity}/{id}")]
public ActionResult Post(RootEntity rootEntity, int id, [FromBody] string message)
{
    ...
}

I had this request:

POST /api/Comment/1/1 HTTP/1.1
Host: localhost:12345
Content-Type: text/plain
Content-Length: 4

test

It resulted in the following Status Code response: 415 Unsupported Media Type

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
    "title": "Unsupported Media Type",
    "status": 415,
    "traceId": "00-e7ca54e9f313c24699c3ca4697b9363d-be4719bd10735245-00"
}

I then switched to Content-Type: application/json like the answer from @BjornBailleul says but got this error instead:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-0549e2c73842c249a93c8dc2f817e250-796e99fc0000224d-00",
    "errors": {
        "$": [
            "'test' is an invalid JSON literal. Expected the literal 'true'. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
        ]
    }
}

Got it working by also encapsulate the string in quotation marks like this: "test".

Complete working request:

POST /api/Comment/1/1 HTTP/1.1
Host: localhost:12345
Content-Type: application/json
Content-Length: 6

"test"

enter image description here

Ogglas
  • 62,132
  • 37
  • 328
  • 418
5

Follow the below steps:

  1. Add to sending request header Content-Type field:

     axios.post(`/Order/`, orderId,
     {
         headers: {'Content-Type': 'application/json'}
     })
    
  2. Every data (simple or complex type) sent with axios should be placed without any extra brackets (axios.post('/Order/', orderId, ...)).

WARNING! There is one exception for string type - stringify it before send (axios.post('/Order/', JSON.stringify(address), ...)).

  1. Add method to controller:

    [HttpPost]
    public async Task<IActionResult> Post([FromBody]int orderId)
    {
        return Ok();
    }
    
1_bug
  • 5,505
  • 4
  • 50
  • 58
5

ASP.NET Core : [FromBody] vs [FromForm]

That unsupported media type error is common when you are trying to send in form field data to ASP.NET via a POST but using one of its specialized WebAPI endpoint handlers that does not accept name-value pairs. [FromBody] is the problem and does not accept traditional HTML5 form field name-value pairs, but requires a JavaScript POST via JSON. Please read on...

Use [FromForm] for traditional HTML form post capture. It can be used to capture either a single form field value from the name-value pair (from the POST HTML data stored in the body of the Http Request) or all the field values in one batch. But you have to modify the WebAPI C# method for each.

In a traditional HTML Form field POST to the server, on submit, the browser sends all the name-value pairs of the form using the Http body of the Request sent to the server. So submitting the following HTML and changing its method to POST sends all your fields to the server as plain text:

    <form id="form1" name="form1" method="post" action="">
        <input type="text" id="field1" name="field1" size="20" value="" /><br />
        <input type="text" id="field2" name="field2" size="20" value="" /><br />
        <input type="text" id="field3" name="field3" size="20" value="" /><br />
        <!-- SEND POST as traditional Name-Value pair. -->
        <button id="formbutton" name="formbutton" form="form1" value="submit">Form Submit (POST)</button>
    </form>

To capture only one of the form field values in a ASP.NET WebAPI you can do the following using [FromForm]...

[HttpPost]
public string EditByPost([FromForm] string myfield1)
{
  return "Success: " + myfield1;
}

...or you can capture all of the form fields as a set using a C# matching object...

[HttpPost]
public ActionResult EditByPost([FromForm] Form1 form)
{
   // Here I am just returning the full set of form fields
   // back as a JSON object.
   return new JsonResult(form);
}

// Here is the C# Class I created that matches the HTML
// form fields being captured by [FormForm]:
public class Form1
{
  public string field1 { get; set; }
  public string field2 { get; set; }
  public string field3 { get; set; }
}

The Strange Creature Called [FromBody]

[FromBody] is different. [FromBody] breaks the traditional HTML form field POST that has worked the same for 20+ years and uses something different!

It works much like [FromForm] does, but it is a new design, requiring JavaScript, custom WebAPI call, or script to post form data formatted in JSON with the JSON mime-type or "Content-Type" Http header in the Request. It is sent via the Http POST inside the body or payload section of the Request, but formatted as JSON with the 'application/json' Content-type, telling your ASP.NET WebAPI Core endpoint method to parse JSON objects, not traditional HTML form name-value pairs.

Because regular HTML4/HTML5 forms cannot POST JSON or its content-type, you must use JavaScript tricks plus decorate your attribute in C# with [FromBody] to do that. This is what confuses people using regular HTML.

The example below is now listening for JSON form data, not name-value pairs. It's not obvious it is doing that as it looks the same as the [FromForm] version...

[HttpPost]
public ActionResult EditByPost([FromBody] Form1 form)
{
    return new JsonResult(form);
}

To post form field data with JSON to the [FromBody] WebAPI above, you have to use JavaScript and extract out the form fields from the HTML manually, then send them with the JSON data and JSON content-type to the server. Most modern JavaScript APIs just bypass HTML and store and send raw JSON data to the listening WebAPI POST now.

Below, I am using a new ECMAScript/JavaScript trick to extract out the same HTML form field values used above. I then call a Promise Object (wrapped by fetch) using async-await, manually add the right JSON header content-type for the listening server, then POST this new form data package directly to the server, bypassing HTML. This might be added to the HTML submit button above as a function to shut down the HTML submit process and use JavaScript instead:

async function JavaScriptPOST() {
  var form = document.getElementById('form1');
  var myfield1 = form.querySelector('#field1').value;
  var myfield2 = form.querySelector('#field2').value;
  var myfield3 = form.querySelector('#field3').value;
  const response = await fetch('https://example.com/editbypost',
  {
    method: 'POST',
    body: JSON.stringify({
      field1: myfield1,
      field2: myfield2,
      field2: myfield3
    }),
    headers: {
      'Content-type':'application/json; charset=UTF-8',
    }
  }).then(...);
}

Realize one thing...traditional HTML name-value POST formats are still the dominant technology around the world. Most HTML forms still post data using traditional POST name-value techniques. JavaScript tricks like this are becoming the new norm, but they do not replace the original HTML form technology and will not via scripting alone.

But knowing how the older technology works always helps when these tech vendors rewrite the technology stack from how it has always worked for decades and decades. Sadly, instead of making it easier or faster or intuitive for developers, they just end up confusing people like this poster.

Stokely
  • 12,444
  • 2
  • 35
  • 23
3

the problem can because of MVC MW.you must set formatterType in MVC options:

services.AddMvc(options =>
            {
                options.UseCustomStringModelBinder();
                options.AllowEmptyInputInBodyModelBinding = true;
                foreach (var formatter in options.InputFormatters)
                {
                    if (formatter.GetType() == typeof(SystemTextJsonInputFormatter))
                        ((SystemTextJsonInputFormatter)formatter).SupportedMediaTypes.Add(
                            Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse("text/plain"));
                }
            }).AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
            });
hamid_reza hobab
  • 925
  • 9
  • 21
2

Another trap of note is making sure you're not decorating controllers with the Consume Attribute as below:

[Produces("application/json")]
[Consumes("application/json")]
public class MyController : Controller

This will fail with a 415 Unsupported Media Type if the upload is NOT JSON.

A "friend of mine" was recently caught out by this like so:

public class MyFileUploadController : MyCustomController {

}

[Produces("application/json")]
[Consumes("application/json")]
public class MyCustomController : ControllerBase {

}
rism
  • 11,932
  • 16
  • 76
  • 116
1

"HTTP 415 Unsupported Media Type response" stems from Content-Type in header of your request. for example in javascript by axios:

Axios({
            method: 'post',
            headers: { 'Content-Type': 'application/json'},
            url: '/',
            data: data,  // an object u want to send
          }).then(function (response) {
            console.log(response);
          });
Mahdi Jalali
  • 323
  • 3
  • 3
0

In my case, I received the HTTP 415 Unsupported Media Type response, since I specified the content type to be TEXT and NOT JSON, so simply changing the type solved the issue. Please check the solution in more detail in the following blog post: https://www.howtodevelop.net/article/20/unsupported-media-type-415-in-aspnet-core-web-api

Kristina Cer
  • 105
  • 9
0

In my case, I had a method in my controller which was requiring an input parameter; unfortunately, the caller (an HttpClient) wasn't passing it. Shame on me.

Francesco B.
  • 2,729
  • 4
  • 25
  • 37
0

You must specify Encoding and Content Type, for instance:

        var request = new HttpRequestMessage
        {
            Method = new HttpMethod("POST"),
            RequestUri = new Uri(CombineUrl(_baseUrl, _resource))
        };
        request.Content = new StringContent(contentBody, System.Text.Encoding.UTF8, "application/json");
Francisco
  • 31
  • 2
0

I copied a POST request & changed to GET in postman, then getting the 415 error.

the fix is to delete the key-value pair in Body, since this is a GET request.

enter image description here

Rm558
  • 4,621
  • 3
  • 38
  • 43