How do I post form data with fetch api?
To quote MDN on FormData
(emphasis mine):
The
FormData
interface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using theXMLHttpRequest.send()
method. It uses the same format a form would use if the encoding type were set to"multipart/form-data"
.
So when using FormData
you are locking yourself into multipart/form-data
. There is no way to send a FormData
object as the body and not sending data in the multipart/form-data
format.
If you want to send the data as application/x-www-form-urlencoded
you will either have to specify the body as an URL-encoded string, or pass a URLSearchParams
object. The latter unfortunately cannot be directly initialized from a form
element. If you don’t want to iterate through your form elements yourself (which you could do using HTMLFormElement.elements
), you could also create a URLSearchParams
object from a FormData
object:
const data = new URLSearchParams();for (const pair of new FormData(formElement)) { data.append(pair[0], pair[1]);}fetch(url, { method: 'post', body: data,}).then(…);
Note that you do not need to specify a Content-Type
header yourself.
As noted by monk-time in the comments, you can also create URLSearchParams
and pass the FormData
object directly, instead of appending the values in a loop:
const data = new URLSearchParams(new FormData(formElement));
This still has some experimental support in browsers though, so make sure to test this properly before you use it.
Client
Do not set the content-type header.
// Build formData object.let formData = new FormData();formData.append('name', 'John');formData.append('password', 'John123');fetch("api/SampleData", { body: formData, method: "post" });
Server
Use the FromForm
attribute to specify that binding source is form data.
[Route("api/[controller]")]public class SampleDataController : Controller{ [HttpPost] public IActionResult Create([FromForm]UserDto dto) { return Ok(); }}public class UserDto{ public string Name { get; set; } public string Password { get; set; }}
You can set body
to an instance of URLSearchParams
with query string passed as argument
fetch("/path/to/server", { method:"POST", body:new URLSearchParams("email=test@example.com&password=pw")})
document.forms[0].onsubmit = async(e) => { e.preventDefault(); const params = new URLSearchParams([...new FormData(e.target).entries()]); // fetch("/path/to/server", {method:"POST", body:params}) const response = await new Response(params).text(); console.log(response);}
<form> <input name="email" value="test@example.com"> <input name="password" value="pw"> <input type="submit"></form>