My code:
fetch("api/xxx", {
body: new FormData(document.getElementById("form")),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
// "Content-Type": "multipart/form-data",
},
method: "post",
}
I tried to post my form using fetch api, and the body it sends is like:
-----------------------------114782935826962
Content-Disposition: form-data; name="email"
test@example.com
-----------------------------114782935826962
Content-Disposition: form-data; name="password"
pw
-----------------------------114782935826962--
(I don't know why the number in boundary is changed every time it sends...)
I would like it to send the data with "Content-Type": "application/x-www-form-urlencoded", what should I do? Or if I just have to deal with it, how do I decode the data in my controller?
To whom answer my question, I know I can do it with:
fetch("api/xxx", {
body: "email=test@example.com&password=pw",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
method: "post",
}
What I want is something like $("#form").serialize() in jQuery (w/o using jQuery) or the way to decode mulitpart/form-data in controller. Thanks for your answers though.
FormData
?
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 the XMLHttpRequest.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; }
}
application/x-www-form-urlencoded
which is what OP is asking for.
Content-Type
from the header and let the browser do it automatically. Thanks!
multipart/form-data
, which is what it should be for form data! Then you can use multer
in expressjs to parse that data format easily.
Use FormData
and fetch
to grab and send data
fetch(form.action, {method:'post', body: new FormData(form)});
function send(e,form) { fetch(form.action, {method:'post', body: new FormData(form)}); console.log('We send post asynchronously (AJAX)'); e.preventDefault(); }
Look on chrome console>network before/after 'submit'
fetch
code that post
the <form>
data, I'm still amazed how I found this. Bye bye jQuery.
csrf
stands for Cross-site Request Forgery.
method: 'post'
has no effect since the browser will use the method attribute of the form
passed to FormData
. Even when the method attribute is not defined in form
the browser will fallback to default GET
method.
mehtod:'post'
from fetch
params in snippet above, you will get Request with GET/HEAD method cannot have body.
exception. If you remove method="POST"
from html in above snippet, the method:'post'
(in fetch params) will have effect - and browser will send POST - I check this by modify this snippet and using chrome>network tab (so actually we can remove this from html... but I will left it)
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); }
Reflect.apply(params.set, params, props)
is a particularly unreadable way of saying params.set(props[0], props[1])
.
Reflect.apply(params.set, params, props)
is readable from perspective here.
With fetch api it turned out that you do NOT have to include headers "Content-type": "multipart/form-data".
So the following works:
let formData = new FormData()
formData.append("nameField", fileToSend)
fetch(yourUrlToPost, {
method: "POST",
body: formData
})
Note that with axios I had to use the content-type.
👨💻These can help you:
let formData = new FormData();
formData.append("name", "John");
formData.append("password", "John123");
fetch("https://yourwebhook", {
method: "POST",
mode: "no-cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "form-data"
},
body: formData
});
//router.push("/registro-completado");
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch(function(error) {
console.log("Error getting document:", error);
});
no-cors
and the Content-Type header are almost certainly wrong.
To add on the good answers above you can also avoid setting explicitly the action in HTML and use an event handler in javascript, using "this" as the form to create the "FormData" object
Html form :
<form id="mainForm" class="" novalidate>
<!--Whatever here...-->
</form>
In your JS :
$("#mainForm").submit(function( event ) {
event.preventDefault();
const formData = new URLSearchParams(new FormData(this));
fetch("http://localhost:8080/your/server",
{ method: 'POST',
mode : 'same-origin',
credentials: 'same-origin' ,
body : formData
})
.then(function(response) {
return response.text()
}).then(function(text) {
//text is the server's response
});
});
@KamilKiełczewski answer is great if you are okay with the form data format being in form multipart style, however if you need the form submitted in query parameter styles:
You can also pass FormData directly to the URLSearchParams constructor if you want to generate query parameters in the way a would do if it were using simple GET submission.
form = document.querySelector('form')
const formData = new FormData(form);
formData["foo"] = "bar";
const payload = new URLSearchParams(formData)
fetch(form.action, payload)
"body:FormData" works but there're type complains, also "FormData" sets multipart headers. To make the things simplier, "body:URLSearchParams" with inline construction and headers set manually may be used :
function getAccessToken(code) {
return fetch(tokenURL,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': '*/*'
},
body: new URLSearchParams({
'client_id':clientId,
'client_secret':clientSecret,
'code':code,
'grant_type': grantType,
'redirect_uri':'',
'scope':scope
})
}
)
.then(
r => return r.json()
).then(
r => r.access_token
)
}
There are instructions on the MDN that the browser will automatically handle Content-Type
.
[A request will also automatically set a Content-Type header if none is set in the dictionary.](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#body)
So we don't need to specify 'content-type' when we send a fetch request.
const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');
formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);
fetch('https://example.com/profile/avatar', {
method: 'PUT',
body: formData
})
.then(response => response.json())
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('Error:', error);
});
If set content-type
in headers. Browser will not try to split formdata in request payload.
I'm using fathcer to handle FormData, the same behavior as XHR.
import { formData } from '@fatcherjs/middleware-form-data';
import { json } from '@fatcherjs/middleware-json';
import { fatcher } from 'fatcher';
fatcher({
url: '/bar/foo',
middlewares: [json(), formData()],
method: 'PUT',
payload: {
bar: 'foo',
file: new File()
},
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then(res => {
console.log(res);
})
.catch(err => {
console.error(error);
});
To post form data with fetch api, try this code it works for me ^_^
function card(fileUri) {
let body = new FormData();
let formData = new FormData();
formData.append('file', fileUri);
fetch("http://X.X.X.X:PORT/upload",
{
body: formData,
method: "post"
});
}
Success story sharing
FormData
in the constructor directly instead of a loop:new URLSearchParams(new FormData(formElement))
URLSearchParams
was very new and had very limited support.fetch
take care of it for you.URLSearchParams
fetch(url, { method: 'post', body: new FormData(form_element), })