Why does Axios not working with FormData? - javascript

I'm using Axios & FormData to send data to a server in a Vue.js project.
It used to work before, but currently, whenever I send a formData object, It doesn't seem to be appended:
Mock server:
Chrome dev tools:
(Notice content-length is 2 and FormData is missing)
let url = "https://benben.free.beeceptor.com/1"
const fd = new FormData()
fd.append('key', 'value')
return axios.post(url, fd, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
Things I've tried:
Look for the payload in: chrome/firefox/edge dev tools & in a mock server.
Send simple JSON instead of FormData - it works.
Send with and without the header - doesn't change anything.
Check if the data is appended to FormData successfully - it is (key-value exists).
Tried other Stackoverflow solutions. Nothing worked so far.
Updating axios.
Using the direct axios API (passing an object to axios).
Note: I need FormData, because I need to upload a file.
Let me know if any other code samples would be helpful.
Thanks in advance!

I've figured it out - I'm using snakecaseKeys package to manipulate the data before sending. It seems not to play well with FormData objects.
import snakecaseKeys from 'snakecase-keys'
axios.defaults.transformRequest = [(data) => {
if (data) {
return snakecaseKeys(data, { deep: true })
}
}, ...axios.defaults.transformRequest]
Thanks for the comments guys!

In case anyone else runs into this issue, you can side-step the transformations in you transformRequest using instanceof.
transformRequest: [
(data) => {
if (data instanceof FormData) return data;
// otherwise, transform your data
return transformedData;
},
],

in React the form should also have encType="multipart/form-data" attribute, may be in vue also?

Related

Reactjs | How to make boundary static when content type is multipart/form-data

We are facing one issue while we are making file upload post call using React-JS, in dev-tools under form data we are getting some browser generated boundary.
------WebKitFormBoundarypSTl3xdAHAJgTN8A
And because of this random boundary we are getting issue while we are making call to a third party api.
Is there any way to make this boundary some fixed value. Something like this :
----somefixedvalue.
Here is the js code:
function doupload() {
let data = document.getElementById("file").files[0];
console.log('doupload',data);
let formData = new FormData();
formData.append("file", data);
fetch('http://localhost:8081/upload/multipart',
{
method:'POST',
body: formData,
headers: {
'Content-Type': 'multipart/form-data; boundary=----somefixedboundary'
}
}).then(res => {
for(const header of res.headers){
console.log(`resHeaderName: ${header[0]}, Value:${header[1]}`);
}
});
alert('your file has been uploaded');
location.reload();
};
can someone help me to solve this? I'm quite confused here as i have given content type along with some static boundary but no luck.
You can convert a file to the binary string and prepare the body by yourself instead of using FormData as it shown in this answer.
But keep in mind that if this ----somefixedvalue substring appears in the file it will be considered a boundary causing body parsing error on receiver side. In case of FormData, browser should take care of it and prevent this.

Getting data as null at the backend when sending an array of objects using formdata in React JS

I want to send an array of objects inside formdata in React JS. When I am appending data inside formdata, it is being received as null at the backend and the API is returning me 500 internal server error. I have tried some of the solutions from the internet but not much support was found. The ways that I have tried till yet are mentioned below:
const surveyFormAnswers = [{Answer: "Hello", QuestionId: 1}, {Answer: "Document", QuestionId: 2, File: file (object)];
const data = new FormData();
data.append('SurveyFormAnswersDtos', JSON.stringify(surveyFormAnswers));
await executeAddSurveyFormAnswers({data: data});
In this case, I am sending a stringified array at the backend (I can see my array being sent inside the browser's Network tab), but at backend, we are using dotnet core5.0. We do not know how exactly can we deseriliaze or parse our request data.
In the second approach, I am appending my data manually inside the formdata (got this approach from the internet) but this is also sending me 500 internal server error from the backend. My code in this case looks like below:
const data = new FormData();
for (var i = 0, valuePair; (valuePair = surveyFormAnswers[i]); i++)
for (var j in valuePair) data.append(j, valuePair[j]);
await executeAddSurveyFormAnswers({data: data});
I am also attaching a postman screenshot so that you can check the required format of data to be sent
Click on the link below to see data being sent using postman
I have been using customized hook for axios and the definition for AddSurveyFormAnswers is below:
export function AddSurveyFormAnswersUrl(token) {
return {
url: `/SurveyAnswer/AddSurveyAnswers`,
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
ContentType:
"multipart/form-data; boundary=<calculated when request is sent>",
},
};
}

Send file with form-data and axios

I am trying to send a video to a videosite, I am able to upload the video using the REST api and postman, so I know the api works as intended. Now I want to do exatcly the same request using axios. I have code that looks like the example on how to use form-data and axios:
const form = new FormData();
const stream = fs.createReadStream(PATH_TO_FILE);
form.append('image', stream);
// In Node.js environment you need to set boundary in the header field 'Content-Type' by calling method `getHeaders`
const formHeaders = form.getHeaders();
axios.post('http://example.com', form, {
headers: {
...formHeaders,
},
})
.then(response => response)
.catch(error => error)
I get the error that data: 'Content-Length is required'
Any ideas?
May be I got your questions wrong , you want to add Content-Length in the header.
I can see you are uploading video stream. So first you have to calculate the data chunk length.
('Content-Length', File.getSize(stream))
Reference: Can I stream a file upload to S3 without a content-length header?
You can make the post request as multi-part type : 'Content-Type': 'multipart/form-data'.
It is preferable way to send large data to server.
You can check this link : How do I set multipart in axios with react?
If I got your question wrong , plese comment or reply . Thanks
The solution to my problem was to set Content-Length accordingly:
"Content-Length": fs.statSync(filePath)['size']
I think the best way to handle this is to actually use the FormData's own method:
const headers = { 'content-length': formData.getLengthSync(), ...formData.getHeaders() }
This will be more accurate because it includes any other data you may add.
To expound, if you are using a ReadStream, you must use the async function instead.
const { promisify } = require('util')
const getLength = promisify(formData.getLength.bind(formData))
const contentLength = await getLength()
const headers = { 'content-length': contentLength, ...formData.getHeaders() }

Put works in Postman but not AXIOS

This is the weirdest thing in my MERN app. When I do a PUT from Postman to my api it works and updates my api and mongoDB. On the front-end it doesn't update the api even though console logs are showing the correct values and the url is the same? Any help or direction would be appreciated... been playing with it for days now.
POSTMAN PROOF UPDATES WORK
The code for my axios is as follows:
handlePlayerSubmit(id, player) {
console.log('This is the id: ' + id);
console.log('This is the player: ' + player);
let apiUrl = 'http://localhost:3001/api/teams';
//sends the id and new author/text to our api
axios.put(`${apiUrl}/${id}`, player).catch(err => {
console.log(err);
});
}
So I know it's firing due to the console logs... not sure why its not updating the api?
Also it's not console.logging an error.
NETWORK SCREEN SHOT IN DEV TOOLS
HEADERS FROM NETWORK TAB:
This is happening because Axios serializes JavaScript objects to JSON. To serialize in application/x-www-form-urlencoded format you will need to use one of the techniques described in the Axios documentation.
I think qs is a nice solution for you:
let apiUrl = 'http://localhost:3001/api/teams';
//sends the id and new author/text to our api
axios.put(`${apiUrl}/${id}`, qs.stringify(player)).catch(err => {
console.log(err);
});
Axios doesn't like posting plain objects.
One way to workaround issue is with FormData:
let formData = new FormData();
formData.append('param1', 'hi');
formData.append('param2', 'hello');
axios({
method: 'POST',
url: '/api/some-endpoint/',
data: formData
}).then(response => {
console.log(response);
});
https://github.com/axios/axios/issues/1195

Request.post on already uploaded image file

I am using Angularjs and nodejs in the project and working on file uploads. I want to send the post request to the url endpoint in a secure way as I need to attach accesstoken with the request. So the way I did this was, I added the directive to choose the file from UI and once it gets the file, I append it using FormData() like this in the controller
var fd = new FormData();
fd.append('file',myFile);
and sending this formdata object to the nodejs server like mentioned here http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs
expect this request will be going to my nodejs server url from there I will be making another post request to external web service
$http.post('api/collections/upload',fd, {
transformRequest: angular.identity,
headers: {
'Content-type': undefined
}
});
So it will attach the right content-type and boundaries in the request. I am getting the file on server side nodejs when I do
function(req,res){
console.log(req.files); //I am able to see the file content
}
It is uploaded on the nodejs server side.
Now I want to make a post request using the req.files to a different endpoint along with proper accessToken and headers. Tried many things but not able to make the request go thru. Not sure how can I attach the imagedata/ req.files along with the request. I tried these two things mentioned in request npm module https://www.npmjs.org/package/request
1)
request.post({
url: 'https://www.example.com/uploadImage',
headers: {
'Authorization': <accessToken>,
'Content-type': 'multipart/form-data'
},
body: req.files
});
Don't know how can I attach and binary data with this request and how can I put boundary. Is boundary needed when you want to send the uploaded image with this request?
2)
fs.createReadStream(req.files.file.path, {encoding: base64}).pipe(request.post({
url: 'https://www.example.com/uploadImage',
headers: {
'Content-type': 'multipart/form-data'
}
}));
Can someone please suggest some ideas on how can I send this post request using request npm module? Thanks.
Documentation here has lots of examples of doing exactly what you describe:
https://github.com/mikeal/request#streaming
As can be seen in that link, the request library adds a .pipe() method to your http's req object, which you should be able to use like the examples in the link:
function(req, res) {
req.pipe(request.post('https://www.example.com/uploadImage');
}
Or something similar.
You were nearly there with your #2 try, but that would only work if you have previously written the file out to disk and were reading it in with fs.createReadStream()
your suggestion helped me to atleast know what I was trying was right. Another article that solved my problem was this http://aguacatelang.wordpress.com/2013/01/05/post-photo-from-node-js-to-facebook/ .Basically, here is what I did and it worked. Thanks for your suggestion.
var form = new FormData();
form.append('file', fs.createReadStream(__dirname + '/image.jpg'));
var options = {
url: 'https://www.example.com/uploadImage?access_token='+ <accesstoken>,
headers: form.getHeaders()
};
form.pipe(request.post(options,function(err,res){
if(err){
log.debug(err);
}
else {
log.debug(res);
}
}));

Categories

Resources