Undesired Format from body-parser Node JS - node.js

I have an android app sending me accelerometer data using the following where body is a string like {"device_name":"device1","time":123123123,"acceleration":1} :
con = (HttpURLConnection) new URL(SERVER).openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
writer = new OutputStreamWriter(con.getOutputStream());
writer.write(body);
writer.flush();
On the server side, I am using body parser like:
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended:false}));
...
app.post('/' function(req,res {
console.log(req.headers);
console.log(req.body);
When I get a post request, this is what shows up:
{ '{"device_name":"device1","time":123,"jerk":21.135843,"acceleration":1}': '' }
I would like to get the req.body in the form of {"device_name":"device1","time":123123123,"acceleration":1} is there a parameter I am missing to set this?
Thanks!
Update:
The client code is inaccessible for change to me, so it'll be much harder to change content type that's being sent. Here's the req.head log...
{ 'user-agent': '...(Linux; U; Android 4.1.2;...)',
host: '...',
connection: 'Keep-Alive',
'accept-encoding': 'gzip',
'content-type': 'application/x-www-form-urlencoded',
'content-length': '...' }

You're uploading a JSON string, but you don't instruct body-parser to handle those.
Instead of this:
app.use(bodyParser.urlencoded({extended:false}));
Use this:
app.use(bodyParser.json());
Also make sure that your request sets the Content-Type header to application/json. If that's not possible, and you're sure that the uploaded content is always going to be JSON, you can force the body parser to parse the body as JSON like this:
app.use(require('body-parser').json({ type : '*/*' }));

Related

ajax requests to Node.js

In my app.js file in Node.js I have this:
app.use(express.json());
which I thought was good enough but whenever I submit data via jQuery/Ajax the response is an empty object.
After doing the below however, it now get the data from req.body. But, I thought that in the newer versions of Express it was not necessary to do this anymore?
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
The Ajax request:
$.ajax({
url: 'http://localhost:8000/api/v1/endpoint',
method: 'POST',
data: {key: key}
})
.done(function(data) {
console.log(data)
})
.fail(function(data) {
console.log(data);
})
The issue is with how you're making your ajax requests. express.json expects the Content-Type of the request to be JSON and I believe if you check your network call on the browser DevTools you'll see that the Content-Type of your request is not JSON.
You need to set the headers in your ajax request and also send valid JSON data like so
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify({key: key})
Then the express.json middleware will handle the request and you won't need to use the bodyParser lines of code you're using.

Parse XML body from HTTP Push request with Express & Node.js, using body-parser-xml

I need to process a HTTP push request using Node.js express.
The request is sending the body in XML format, that's why I chose the body-parser-xml package for parsing.
My problem is, that the body isn't properly parsed – I guess because the package doesn't recognize the mime type of the transferred body.
The endpoint:
const express = require('express');
const bodyParser = require('body-parser');
require('body-parser-xml')(bodyParser);
const app = express();
const PORT = 8085;
app.use(express.urlencoded({ extended: true }));
app.use(bodyParser.xml({
limit:'25MB'
}));
app.post('/feed', function (req, res, body) {
console.log(req.headers);
console.log(req.body);
res.status(200).end();
});
The output:
{
host: 'localhost:8085',
accept: '*/*',
'x-meta-feed-type': '1',
'x-meta-feed-parameters': 'feed params',
'x-meta-default-filename': 'filename.xml',
'x-meta-mime-type': 'text/xml',
'content-length': '63'
encoding: 'UTF-8',
connection: 'Keep-Alive'
}
{
'<data id': '"1234"><name>Test</name><title>Test1234</title></data>'
}
I'm not able to change the request itself (it's external), only the Node.js endpoint.
Any idea how to process the content properly?
Thanks for your help!
The request has apparently been parsed by the
app.use(express.urlencoded({ extended: true }));
middleware, which means that it must have had Content-Type: application/x-www-form-urlencoded. Remove the two app.use lines, because they make "global" body-parsing decisions (for every request), whereas you need a special treatment only for one type of request.
If you instantiate the XML body parser with the "non-standard" (that is, wrong) type, it will parse the content as XML:
app.post('/feed',
bodyParser.xml({type: "application/x-www-form-urlencoded"}),
function (req, res) {
console.log(req.headers);
console.log(req.body);
res.status(200).end();
});

express (using multer) Error: Multipart: Boundary not found, request sent by POSTMAN

Notice: only when I use form-data body form in Postman (which is the form I have to use because I want to send files beside text fields), I get:
Error: Multipart: Boundary not found.
when I use x-www-form-urlencoded everything is ok. (ofcourse when body-parser is used as middleware)
This is Request Content: (made by Postman)
POST /test HTTP/1.1
Host: localhost:3000
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Cache-Control: no-cache
Postman-Token: 61089a44-0b87-0530-ca03-a24374786bd1
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="test"
a simple word
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="data"
good
------WebKitFormBoundary7MA4YWxkTrZu0gW--
index.js:
var express = require('express');
var app = express();
var multer = require('multer');
var upload = multer();
app.post('/test', upload.array(), function (req, res, next) {
console.log(req.body.test);
console.log(req.body);
});
app.listen(3000, function () {
console.log('app started');
});
I found the solution. I only had to prevent Postman to send a Content-Type header. So I just removed it from request headers.
To give some insight on why that is happening,
When using content type multipart/form-data in any HTTP request, you can add a boundary information alongside the Content-Type specification like:
Content-Type: multipart/form-data; boundary=MyBoundary
You can replace MyBoundary with any string of your liking.
Then you will have to encode your form data (name=Abebe&age=5) as:
--MyBoundary
Content-Disposition: form-data; name="name"
Abebe
--MyBoundary
Content-Disposition: form-data; name="age"
5
--MyBoundary--
For more info read this StackOverflow question and answer
For JMeter and postman remove Content-Type from header.
it will resolve your issue.
I am going to expand a little bit on user9150719 for those who are having the same issue with the frontend side of things and are wondering where to remove the headers.
I had the same issue; I was trying to post from an Angular app to my Nodejs server. my post request included raw data and a file input. So I was thinking FormData()
Angular Service
//Declare header variables.
formDataHeader = {
headers: new HttpHeaders({
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + this._myService.getToken()
})
};
//Post function to Nodejs server
addNewContact(contact: FormData): any {
return this._httpClient.post(
environment.apiBaseUrl + '/contacts', // POST /api/contacts
(contact), // contact data,
this.formDataHeader
);
}
My formData was setup properly. I was able to get all the data, but the problem is that I had setup couple headers in my request that resulted in what user9150719 was experiencing.
My solution was to simplify my headers to this:
formDataHeader = {
headers: new HttpHeaders({
Authorization: 'Bearer ' + this._myService.getToken()
})
};
Another important thing to point out is that I didn't need to set the enctype="multipart/form-data" on my <form></form> tag.
Even though I had an httpInterceptor setup (I don't think it is working properly), I still needed to add the Authorization header on all my requests, but all other headers were resulting in my api call to return unexpected results.
Finally I think (but I am not entirely sure) that the reason why I didn't need to setup extra headers, is because in my NodeJS server, I already configured what headers to expect.
Node.JS Server
// app.js
app.use('/public/uploads', express.static('uploads'));
app.use('/public', express.static('public'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(cors());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'x-www-form-urlencoded, Origin, X-Requested-With, Content-Type, Accept, Authorization, *');
if (req.method === 'OPTIONS'){
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, PATCH, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Credentials', true);
return res.status(200).json({});
}
next();
});
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.use(bodyParser.json({limit: '50mb', extended: true}));
So I think that if your server is setup to handle certain types of headers (Content-Type, Authorization, Origin, etc.), You don't necessarily need to set those headers again on your frontend when you send your request to the server. There are certain exceptions, such Authorization which in certain cases need to be set; probably because they carry some data in the form of token or something in that regards.
I hope this helps someone out there!
If you are using fetch method
fetch("http://localhost:4000/upload_files", {
method: 'POST',
body: formData,
headers: {
"Content-Type": "multipart/form-data"
}
})
headers so that Fetch api automatically set the headers. Now remove headers or "Content-Type": "multipart/form-data"
fetch("http://localhost:4000/upload_files", {
method: 'POST',
body: formData
})
Now it works
Don't mention CONTENT-TYPE header while uploading files from FE using axios/fetch or anything 3rd HTTP handler.
Reason bcoz we don't know the boundary of our files. If you pass only 'multipart/form-data' as Content-Type, you will get an error since we aren't passing boundary in there.
So, let the browser add it (multipart/form-data) along with Boundary based on the files attached.
AND if you want to send some data along with files, you should be sending them as a multipart/form-data(Again we don't need to add this header manually) type only.
We CANNOT send multiple Content-Type data at once for any http call.
Please refer below code.
async onUpload(){
const formData=new FormData();
formData.append('files', file1);
formData.append('files', file2);
formData.append('data', {key1:value1, key2:value2});
const res=await axios.post(URL, formData);
}
FYI #hassam-saeed
Just if someone has the same issue i had.
NestJs - BackEnd
NextJs - FrontEnd
I was trying to do something like:
const formData = new FormData()
formData.append('context', body.context.toString())
body.files.map((file) => {
formData.append('files', file, `${file.name}`)
})
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
...(access_token ? { Authorization: `Bearer ${access_token}` } : {}),
},
body: formData,
})
But because this 'Content-Type' overrides the browsers setting of 'Content-Type' AND the content-length is not explicitly set (which was the real issue i think) ,the form-data was showing up on the backend still encoded. So NestJS was not able to parse the 'context' variable or the 'files'.
Also, if you use api routes in NextJS, remeber to check that no-where in there is the 'Content-Type' overridden.... I also had this issue.
I did this instead
const form = new FormData();
headers['Content-Type'] = `multipart/form-data; boundary=${form._boundary}`;
Source: https://stackoverflow.com/a/54796556/8590519

Receive empty object in node js express request

I have a problem when i upload image from react native to node js server, this is my code
Client
var url = "http://192.168.55.120:3000/uploadimg";
var file = this.state.imgsrc.uri;
const data = new FormData();
data.append('token', 'testName');
data.append('photo', {
uri: file,
type: 'image/jpeg',
name: 'testPhotoName'
});
fetch(url, {
method: 'post',
headers: {
'Accept': 'application/json, application/xml, text/plain, text/html, *.*',
'Content-Type': 'multipart/form-data'
},
body: data
}).then(res => {
console.log(res)
});
and my server code
const express = require('express'),
bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/uploadimg', function (req, res) {
console.log(req.body); // =====> empty object
})
My code is wrong ?
As you can check in body-parser docs, this package doesn't handle multipart form data bodies.
This does not handle multipart bodies, due to their complex and
typically large nature. For multipart bodies, you may be interested in
the following modules:
busboy and connect-busboy
multiparty and connect-multiparty
formidable
multer
Also, you shouldn't need to specify the content type header in fetch; it'll handle it for you depending on the provided body.
You cannot stringify the formData as mentioned in their docs
Using the FormData API is the simplest and fastest, but has the disadvantage that data collected can not be stringified.
If you want to stringify a submitted data, use the pure-AJAX method.
You may consider trying it with util-inspect

In nodejs how to send cookies in a request to a server

how can we receive a cookie as the 'set-cookie' parameter of the response and header and then send this cookie in next request.All this using the 'http' module and no 3rd party modules.I am basically interested on how i will post the cookie as a parameter in the header about to go in the next request
oh finally found my mistake i wasnt including the cookie parameter in my header list.I was writing it like this:
var options = {host:url_parsed.host, path:url_parsed.path, method:'GET', 'Cookie':cookie, 'Accept':'/', 'Connection':'keep-alive', };
I should actually be like this:
var options = {host:url_parsed.host, path:url_parsed.path, method:'GET', headers:{'Cookie':cookie}, 'Accept':'/', 'Connection':'keep-alive', };
You can write a cookie in the header like this:
response.writeHead(200, {
'Set-Cookie': 'mycookie=testvalue',
'Content-Type': 'text/plain'
});
response.end('Hello World\n');

Resources