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.