axios converts x-www-form-urlencoded to multipart/form-data - javascript

I am sending a post request with axios. However, when I check the browser console, I see that the request header is actually content-type: multipart/form-data. How can I enforce application/x-www-form-urlencoded? Or does it even matter?
let data = new FormData();
data.append('grant_type', 'authorization_code');
// ... removed for conciseness
return axios.post(`${AUTH_URL}/token`,
data,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}

FormData objects always serialise to multipart/form-data. They have to because they support file uploading and application/x-www-form-urlencoded and application/json do not.
If you want to send application/x-www-form-urlencoded data you should pass a URLSearchParams object instead.
This is described in the axios documentation.
In either case, you shouldn't specify the Content-Type in the headers since the underlying browser APIs will infer it correctly from the object type passed as the body data.

Related

"axios.defaults.headers.common['Content-Type'] = 'application/json'" but axios.post still is "application/x-www-form-urlencoded"

I set axios common header Content-Type = application/json by below code:
axios.defaults.headers.common['Content-Type'] = 'application/json';
But when I tried to use axios.post('apiurl', json), browser Content-Type still is application/x-www-form-urlencoded
I tried to use axios.post('Get_Office_PO_NO_Data', json, { headers: { 'content-type': 'application/json' } }) and axios can request application/json content type
Per the documentation, you want post, not common:
axios.defaults.headers.post['Content-Type'] = 'application/json';
// −−−−−−−−−−−−−−−−−−−−^^^^
Which makes sense; other request types (GET, DELETE, etc.) don't have any request body to apply the content type to. (PUT and PATCH do, but I guess they figured it was unlikely you'd want to set the same default content type for POST and PUT, or it was just the whim of the developer.)
(Alternatively you might consider creating an instance with your defaults via axios.create, and then using the instance. Changing global defaults always makes me a bit uneasy. :-) )

Why does Axios send my POST request with Content-Type application/x-www-form-urlencoded when using a string as the request body?

I found this interesting behavior with axios while making POST requests. I am sending URLs that the user types in to my Spring RestController which accepts the request body as a String. It looks like this:
#PostMapping("user/{userId}/link")
public ResponseEntity<UserLinkDTO> addUserLink(
#PathVariable(value = "userId") Integer userId, #RequestBody String url) {
...
}
On the front end, my axios request looks like this:
const saveUserLink = async (userId: number, url: string): Promise<UserLinkDTO> => {
return axiosInstance.post(`user/${userId}/link`, url))
.then(r => r.data)
);
When I send some arbitrary URLs (with = in them) like these:
https://www.google.com/search?client=firefox
https://www.yahoo.com/somePage?key=value&hello=world
the Spring POST method receives the url exactly as it was sent.
However, when I send some URLs like this (without any = in them):
https://www.google.com
https://www.yahoo.com/somePage
my Spring POST method receives the url with = appended to them! So the links are received as:
https://www.google.com=
https://www.yahoo.com/somePage=
I noticed the requests are being sent with Content-Type: application/x-www-form-urlencoded in my Network tab. However, based on axios docs, all requests should be serialized to JSON which would imply being sent with Content-Type: application/json. All the other requests I'm using are sent with Content-Type: application/json. It's only this one that gets converted to Content-Type: application/x-www-form-urlencoded.
I edited my axios post request to include some custom headers so I could change the Content-Type back to application/json:
const saveUserLink = async (userId: number, url: string): Promise<UserLinkDTO> => {
return axiosInstance.post(`user/${userId}/link`, url, {
headers: {
'Content-Type': 'application/json',
}
})
.then(r => r.data)
);
My Spring POST method is now able to receive the URLs above exactly as provided, regardless of if they have = in them or not. There is no longer an extra = appended to the links without any =.
Why is it that axios seems to change the Content-Type from application/json to application/x-www-form-urlencoded when providing a string as the request body?
the default content-type in axios is 'application/x-www-form-urlencoded'
//https://github.com/axios/axios/blob/master/lib/defaults.js
var DEFAULT_CONTENT_TYPE = {
'Content-Type': 'application/x-www-form-urlencoded'
};
//...
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
while when request's data is an Object, axios sets contentType to 'application/json':
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}

Using FormData() inside fetch api is not working

I read all over but couldn't find the answer.
When I use FormData(), it returns status 404 bad request.
However, if I pass the data (hardcoded) as in const requestBody (example below), it works perfectly.
This is my code:
var formData = new FormData();
formData.append("nickname", "johxns");
formData.append("password", "john_password");
formData.append("email", "john#server.com");
// If I do it this way, and assign this to body inside fetch, it works perfectly
// const requestBody = '{"nickname": "johxns","password":"john_password","email":"john#server.com"}';
fetch("http://localhost:5000/create_user", {
// if instead of formData, I assign requestBody to body, it works!
body: formData,
headers: {
"Content-Type": "application/json"
},
method: "POST"
}).then(function(response) {
return response.text();
}).then(function(data){
console.log('data', data);
}).catch(function(err){
console.err(err);
});
I already tried with URLSearchParams, but still couldn't make it work.
Thanks.
You shouldn't set the Content-Type header to application/json if you're not sending json. According to this answer, you don't need to set the Content-Type header.
body data type must match "Content-Type" header
Using Fetch
You should either send json data if you set Content-Type to application/json or not set any Content-Type if using FormData API since the fetch function is able to determine the correct Content-Type.
See here for more informations.

Redirect With Post data (json) and has headers

I want to redirect with a json data and has headers, I have tried jquery.redirect but I do not know how to put headers (like 'content-type': 'application/json') ...
This is my code :
$.redirect('url', data, 'POST', '_blank');
any suggestions?
You can't.
The only time you can specify HTTP request headers when using browser-side JavaScript is when you are using fetch or XMLHttpRequest (i.e. Ajax).
(You can also use enctype in a form submission, but not with application/json as the value).

fetch - Missing boundary in multipart/form-data POST

I want to send a new FormData() as the body of a POST request using the fetch api
The operation looks something like this:
var formData = new FormData()
formData.append('myfile', file, 'someFileName.csv')
fetch('https://api.myapp.com',
{
method: 'POST',
headers: {
"Content-Type": "multipart/form-data"
},
body: formData
}
)
The problem here is that the boundary, something like
boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu
never makes it into the Content-Type: header
It should look like this:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu
When you try the "same" operation with a new XMLHttpRequest(), like so:
var request = new XMLHttpRequest()
request.open("POST", "https://api.mything.com")
request.withCredentials = true
request.send(formData)
the headers are correctly set
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu
So my questions are:
how do I make fetch behave exactly like XMLHttpRequest in this situation?
if this is not possible, why?
Thanks everybody! This community is more or less the reason I have professional success.
The solution to the problem is to explicitly set Content-Type to undefined so that your browser or whatever client you're using can set it and add that boundary value in there for you. Disappointing but true.
I removed "Content-Type" and added 'Accept' to http headers and it worked for me. Here are the headers I used,
'headers': new HttpHeaders({
// 'Content-Type': undefined,
'Accept': '*/*',
'Authorization':
"Bearer "+(JSON.parse(sessionStorage.getItem('token')).token),
'Access-Control-Allow-Origin': this.apiURL,
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
'Access-Control-Allow-Headers': 'origin,X-Requested-With,content-type,accept',
'Access-Control-Allow-Credentials': 'true'
})
fetch(url,options)
If you set a string as options.body, you have to set the Content-Type in request header ,or it will be text/plain by default.
If options.body is specific object like let a = new FormData() or let b = new URLSearchParams(), you don't have to set the Content-Type by hand.It will be added automaticlly.
for a ,it will be something like
multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
as you see, the boundary is automaticlly added.
for b, it is application/x-www-form-urlencoded;
I had the same issue, and was able to fix it by excluding the Content-Type property, allowing the browser to detect and set the boundary and content type automatically.
Your code becomes:
var formData = new FormData()
formData.append('myfile', file, 'someFileName.csv')
fetch('https://api.myapp.com',
{
method: 'POST',
body: formData
}
)
Add headers:{content-type: undefined} browser will generate a boundary for you
that is for uploading a file part-and-part with streaming
if you are adding 'multiple/form-data' it means you should create streaming and upload your file part-and-part
So it is okay to add request.headers = {content-type: undefined}
I'm using the aurelia-api (an wrapper to aurelia-fetch-client).
In this case the Content-Type default is 'application/json'. So I set the Content-Type to undefined and it worked like a charm.
According to FormData documentation, you shoudn't manually set the Content-Type header so browser itself will set it correctly:
Warning: When using FormData to submit POST requests using XMLHttpRequest or the Fetch_API with the multipart/form-data Content-Type (e.g. when uploading Files and Blobs to the server), do not explicitly set the Content-Type header on the request. Doing so will prevent the browser from being able to set the Content-Type header with the boundary expression it will use to delimit form fields in the request body.
So if your code (or library/middleware/etc) manually set the Content-Type, you have two ways to fix it:
rewrote your code (or whatever you use) to don't set Content-Type by default
set Content-Type to undefined or remove it from headers to let your browser do it's work

Categories

Resources