How to Allow Form-Data in NodeJS - node.js

I recently created an API that accepts files. I am trying to test the API using Postman. If I make a post request using x-wwww-form-urlencoded body type, everything works and I get all the expected data. The only problem is that it does not allow to send a file. If I use form-data body type, that allows you to send files, I don't receive anything at the back-end. Not sure if something is wrong with Postman or if I am doing something wrong. My hunch is that the back-end doesn't accept form-data currently, that is why I am not receiving any data. Anything I could do to change that?
My headers look something like this so far,
res.setHeader('Access-Control-Allow-Origin', origin);
res.header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, form-data");
app.js
app
// static route will go to the client (angular app)
.use(express.static('client'))
// secured routes
.use('/api', secured_route)
// add a user route
.post('/user', user_api_controller.add_user)
// delete this in the production
.use(function(req, res, next) {
res = allowed_orgins(req, res);
next();
})
;
allowed_orgins = function (req, res){
var allowedOrigins = ['http://localhost:4200', 'http://localhost:8100'];
var origin = req.headers.origin;
if(allowedOrigins.indexOf(origin) > -1){
res.setHeader('Access-Control-Allow-Origin', origin);
res.header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, multipart/form-data");
}
return res;
}
user_api_controller.js
module.exports.add_user = function (req, res) {
console.log(req.body.username);
console.log(req.body.other_parameters);
}
I get nothing print out on console if I use form-data, but it works fine when I use x-wwww-form-urlencoded.
I've used multer middle ware, I am still not getting anything. Middle ware comes into play when my back-end will receive something. I have tried getting the plain text fields of form-data, and I am not getting that either. That means my back-end is unable to receive all form-data fields, not just files but the text fields as well.

Here my super simple express example:
const express = require('express')
const fileUpload = require('express-fileupload');
const app = express()
app.use(fileUpload());
app.post('/file-upload', function(req, res, next) {
console.log(req.body.msg);
console.log(req.files);
res.send('ok');
next();
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
Sorry I don't use postman, but I use curl and it works for me:
curl http://localhost:3000/file-upload \
-H "Content-Type: multipart/form-data" \
-F "file=#/YourDir/YourFile.txt" \
-F "msg=MyFile"
So you can try it, test it, get the gist and play with your app using this curl command.

The Content-Type header for file upload is actually multipart/form-data not form-data. To upload a file appropriately in Postman please have a look here: https://github.com/postmanlabs/postman-app-support/issues/1441#issuecomment-289452571
In the backend, you would need a library to handle multipart/form-data request. If you're using expressjs then this library would fit your need https://www.npmjs.com/package/multer
Here is how to use the multer library
1) First install multer
npm install --save multer
2) Include the library in your code (note that the uploads directory must be existing)
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
3) Use upload as a middleware for your add_user endpoint (fill in the parentheses with your field name that contain uploaded file)
app
// other endpoints
.post('/user', upload.single(/* file field name in your form */), user_api_controller.add_user)
4) The file can be accessed in your user_api_controller.js
module.exports.add_user = function (req, res) {
console.log(req.file);
}

Related

CORS Issue: Unable to send post request using NodeJs and Angular

I am getting this message when trying to send a post request:
Access to XMLHttpRequest at 'http://localhost:3002/api/products/checkout' from origin
'http://localhost:4200' has been blocked by CORS policy: Request header field content-type
is not allowed by Access-Control-Allow-Headers in preflight response.
Right now I'm simply trying to send data to my backend and then log it in the console. Get requests work fine but for some reason I get that CORS error when trying post. Here is my code:
Angular code:
//api call
return this.http.post('http://localhost:3000/api/checkout', cart)
NodeJs code:
const bodyParser = require('body-parser');
const express = require('express');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader(
'Access-Control-Allow-Header',
'Origin, X-Requested-With, Content-Type, Accept');
res.setHeader(
'Access-Control-Allow-Methods',
'GET, POST, PATCH, DELETE, OPTIONS');
next();
})
app.post("/api/checkout", (req, res, next) => {
const cart = req.body;
console.log(cart)
res.status(201).json()
})
module.exports = app;
In the network calls I can see that Request Headers is:
Access-Control-Request-Headers: content-type
while Response Headers is:
Access-Control-Request-Headers: Origin, X-Requested-With, Content-Type, Accept
I'm not sure if content-type being lower case has anything to do with the issue.
You should use req.set instead, just change setHeader to set only. Here is the document https://expressjs.com/en/api.html#res.set
And if you just using localhost, there's another easier option, you can use proxy. More information can be found here https://angular.io/guide/build#proxying-to-a-backend-server
I think the problem that you wrote Access-Control-Allow-Header instead of Access-Control-Allow-Headers, but I cannot test it now.
Be aware that you need to serve OPTIONS requests too. Which is just responding with an empty message body using the same headers. You can get these kind of OPTIONS requests before PUT, PATCH, DELETE from certain HTTP clients e.g. from browsers too.

NodeJS req.body is always empty

My server.js code:
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Origin', '*');
next();
});
const AboutController = require('./controllers/AboutController');
app.use('/about', AboutController);
AboutController.js
router.post('/store', (req, res, next) => {
// GETTING REQUEST DETAILS AND INITIALIZATION
let content = req.body.content;
let company_name = req.body.company_name;
console.log(req.body);
});
module.exports = router;
Problem
req.body always return {} which is empty object and I don't know why!
What I have tried
I have tried to console.log(req) and it returned objects but still the body object is empty!
Client Side Request
I am using Postman form-data to simulate client request.
The reason your Postman request didn't work is because you aren't parsing a form-data type anywhere in your app. The only middleware that you have for parsing is bodyParser.json(), which as it says on the tin, parses JSON. For example, if you wanted to parse x-www-form-urlencoded, you could use bodyParser.urlencoded().
If you want what you currently have to work, you need to send it JSON, not form-data
Regardless, the form-data type is normally used for larger payloads (ex: files)
I found the solution..
I was using Postman and I was using form-data instead of raw and json and it worked fine I don't know why yet!

Angular express.js CORS

I am using express 4.1.0, and angular 1.2.12.
My client-code looks like this:
$http.get(data_url + '/stickers.json')
.success(function (data, status, headers, config) {
// do stuff
})
.error(function (data, status, headers, config) {
console.log(status, config);
});
I have done all the correct express stuff, as far as I can tell:
var express = require('express'),
app = express();
app.use(function (req, res, next) {
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.set('Access-Control-Allow-Headers', 'X-Requested-With,Content-Type,Accept');
next();
});
app.use(express.static(__dirname + '/webroot'));
app.listen(process.env.PORT || 5000);
I also tried cors module.
I am running my client-app on localhost, port 8000. The remote file is on heroku.
In all cases, I get this error in the console:
XMLHttpRequest cannot load http://<URL>/stickers.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access.
When I access http://<URL>/stickers.json directly and look at returned headers, I see these:
Access-Control-Allow-Headers:X-Requested-With,content-type
Access-Control-Allow-Methods:GET, POST, OPTIONS, PUT, PATCH, DELETE
Access-Control-Allow-Origin:*
Update:
If you'd like to look at it, I made a codepen, and a new heroku app with above server code.
Weirdly, it works fine, so now I am really confused. I think it must be express, because it stops working when I swap the URLs.
Update 2:
If I run the codepen with the URL from localhost running the original code, it also works. It must be something funny with heroku.
I got it! it needs to be http://*.herokuapp.com/, or it redirects. I was using *.heroku.com.

No 'Access-Control-Allow-Origin' - Node / Apache Port Issue

i've created a small API using Node/Express and trying to pull data using Angularjs but as my html page is running under apache on localhost:8888 and node API is listen on port 3000, i am getting the No 'Access-Control-Allow-Origin'. I tried using node-http-proxy and Vhosts Apache but not having much succes, please see full error and code below.
XMLHttpRequest cannot load localhost:3000. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'localhost:8888' is therefore not allowed access."
// Api Using Node/Express
var express = require('express');
var app = express();
var contractors = [
{
"id": "1",
"name": "Joe Blogg",
"Weeks": 3,
"Photo": "1.png"
}
];
app.use(express.bodyParser());
app.get('/', function(req, res) {
res.json(contractors);
});
app.listen(process.env.PORT || 3000);
console.log('Server is running on Port 3000')
Angular code
angular.module('contractorsApp', [])
.controller('ContractorsCtrl', function($scope, $http,$routeParams) {
$http.get('localhost:3000').then(function(response) {
var data = response.data;
$scope.contractors = data;
})
HTML
<body ng-app="contractorsApp">
<div ng-controller="ContractorsCtrl">
<ul>
<li ng-repeat="person in contractors">{{person.name}}</li>
</ul>
</div>
</body>
Try adding the following middleware to your NodeJS/Express app (I have added some comments for your convenience):
// Add headers before the routes are defined
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8888');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
(You might need to use 127.0.0.1 instead of localhost.)
Accepted answer is fine, in case you prefer something shorter, you may use a plugin called cors available for Express.js.
It's simple to use, for this particular case:
var cors = require('cors');
// use it before all route definitions
app.use(cors({origin: 'http://localhost:8888'}));
(You might need to use 127.0.0.1 instead of localhost.)
The request origin needs to match the allowed origin(s), and you can also have multiple of them:
app.use(
cors({origin: ['http://localhost:8888', 'http://127.0.0.1:8888']})
);
Another way, is simply add the headers to your route:
router.get('/', function(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // If needed
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type'); // If needed
res.setHeader('Access-Control-Allow-Credentials', true); // If needed
res.send('cors problem fixed:)');
});
The top answer worked fine for me, except that I needed to whitelist more than one domain.
Also, top answer suffers from the fact that OPTIONS request isn't handled by middleware and you don't get it automatically.
I store whitelisted domains as allowed_origins in Express configuration and put the correct domain according to origin header since Access-Control-Allow-Origin doesn't allow specifying more than one domain.
Here's what I ended up with:
var _ = require('underscore');
function allowCrossDomain(req, res, next) {
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
var origin = req.headers.origin;
if (_.contains(app.get('allowed_origins'), origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
if (req.method === 'OPTIONS') {
res.send(200);
} else {
next();
}
}
app.configure(function () {
app.use(express.logger());
app.use(express.bodyParser());
app.use(allowCrossDomain);
});
The answer code allow only to localhost:8888. This code can't be deployed to the production, or different server and port name.
To get it working for all sources, use this instead:
// Add headers
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
Install cors dependency in your project:
npm i --save cors
Add to your server configuration file the following:
var cors = require('cors');
app.use(cors());
It works for me with 2.8.4 cors version.
Hi this happens when the front end and backend is running on different ports. The browser blocks the responses from the backend due to the absence on CORS headers. The solution is to make add the CORS headers in the backend request. The easiest way is to use cors npm package.
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
This will enable CORS headers in all your request. For more information you can refer to cors documentation
https://www.npmjs.com/package/cors
This worked for me.
app.get('/', function (req, res) {
res.header("Access-Control-Allow-Origin", "*");
res.send('hello world')
})
You can change * to fit your needs. Hope this can help.
All the other answers didn't work for me. (including cors package, or setting headers through middleware)
For socket.io 3^ this worked without any extra packages.
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
app.all('*', function(req, res,next) {
/**
* Response settings
* #type {Object}
*/
var responseSettings = {
"AccessControlAllowOrigin": req.headers.origin,
"AccessControlAllowHeaders": "Content-Type,X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name",
"AccessControlAllowMethods": "POST, GET, PUT, DELETE, OPTIONS",
"AccessControlAllowCredentials": true
};
/**
* Headers
*/
res.header("Access-Control-Allow-Credentials", responseSettings.AccessControlAllowCredentials);
res.header("Access-Control-Allow-Origin", responseSettings.AccessControlAllowOrigin);
res.header("Access-Control-Allow-Headers", (req.headers['access-control-request-headers']) ? req.headers['access-control-request-headers'] : "x-requested-with");
res.header("Access-Control-Allow-Methods", (req.headers['access-control-request-method']) ? req.headers['access-control-request-method'] : responseSettings.AccessControlAllowMethods);
if ('OPTIONS' == req.method) {
res.send(200);
}
else {
next();
}
});
Add following code in app.js of NODEJ Restful api to avoid "Access-Control-Allow-Origin" error in angular 6 or any other framework
var express = require('express');
var app = express();
var cors = require('cors');
var bodyParser = require('body-parser');
//enables cors
app.use(cors({
'allowedHeaders': ['sessionId', 'Content-Type'],
'exposedHeaders': ['sessionId'],
'origin': '*',
'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
'preflightContinue': false
}));
You could use cors package to handle it.
var cors = require('cors')
var app = express()
app.use(cors())
for setting the specific origin
app.use(cors({origin: 'http://localhost:8080'}));
know more
You can use "$http.jsonp"
OR
Below is the work around for chrome for local testing
You need to open your chrome with following command. (Press window+R)
Chrome.exe --allow-file-access-from-files
Note : Your chrome must not be open. When you run this command chrome will open automatically.
If you are entering this command in command prompt then select your chrome installation directory then use this command.
Below script code for open chrome in MAC with "--allow-file-access-from-files"
set chromePath to POSIX path of "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
set switch to " --allow-file-access-from-files"
do shell script (quoted form of chromePath) & switch & " > /dev/null 2>&1 &"
second options
You can just use open(1) to add the flags: open -a 'Google Chrome' --args --allow-file-access-from-files
/**
* Allow cross origin to access our /public directory from any site.
* Make sure this header option is defined before defining of static path to /public directory
*/
expressApp.use('/public',function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
// Request headers you wish to allow
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
// Set to true if you need the website to include cookies in the requests sent
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
/**
* Server is about set up. Now track for css/js/images request from the
* browser directly. Send static resources from "./public" directory.
*/
expressApp.use('/public', express.static(path.join(__dirname, 'public')));
If you want to set Access-Control-Allow-Origin to a specific static directory you can set the following.
Apart from all listed answers, I had the same error
I have both access to frontend and backend, I already added cors module app.use(cors()); Still, I was struggling with this error.
After some debugging, I found the issue. When I upload a media which size was more than 1 MB then the error was thrown by Nginx server
<html>
<head>
<title>413 Request Entity Too Large</title>
</head>
<body>
<center>
<h1>413 Request Entity Too Large</h1>
</center>
<hr>
<center>nginx/1.18.0</center>
</body>
</html>
But in the console of frontend, I found the error
Access to XMLHttpRequest at 'https://api.yourbackend.com' from origin 'https://web.yourfromntend.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
So It makes confusion here. But the route cause of this error was from nginx configuration. It's just because the directive client_max_body_size value has been set to 0 by default. It determines what the allowable HTTP request size can be is client_max_body_size. This directive may already be defined in your nginx.conf file located at /etc/nginx/nginx.conf Now you need to add/edit the value of the directive client_max_body_size either at http or server.
server {
client_max_body_size 100M;
...
}
Once you have set your desired value, save your changes and reload Nginx: service nginx reload
After these changes, It's working well
REFERENCE: https://www.keycdn.com/support/413-request-entity-too-large#:~:text=%23,processed%20by%20the%20web%20server.&text=An%20example%20request%2C%20that%20may,e.g.%20a%20large%20media%20file).
We'll see if the top 2 answers accept my edit, but it's very likely you're gonna have to either add or use 127.0.0.1 instead of localhost.
With the cors package, you're even able to use more than one allowed origin:
app.use(
cors({ origin: ["http://localhost:8888", "http://127.0.0.1:8888"] })
);
And you could use origin: "*" if you wish to allow for anything.
For more info, do check out Web Dev Simplified's tutorial.

Handling text/plain in Express (via connect)?

I am using Express 3, and would like to handle text/plain POSTs.
Express 3 uses connect's bodyParser now (I think the old Express code got moved to connect). The documentation for bodyParser gives some details about how to make it support additional file types. And I found an excellent blog post about how handling text/plain was done in old versions of Express).
Should I explicitly require connect (and let node's require cache the modified version)? Or is connect exposed via express somewhere?
connect.bodyParser does not have a 'parse' key.
How can I make Express (via connect) handle text/plain POSTs?
With bodyParser as dependency, add this to your app.js file.
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.text());
Happy Noding.
https://gist.github.com/3750227
app.use(function(req, res, next){
if (req.is('text/*')) {
req.text = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ req.text += chunk });
req.on('end', next);
} else {
next();
}
});
Will add the text as req.text
In express.js "^4.16..." the following code works fine for me:
// parse an HTML body as a string
app.use(bodyParser.text({ type: 'text/*' }))
The extended piece of the code is below:
// parse an HTML body as a string
app.use(bodyParser.text({ type: 'text/*' }))
// Enable CORS for ExpressJS
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
res.header('Access-Control-Allow-Credentials', true)
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Methods, Credentials')
next()
})
// Api url
app.post('/api/myApi', (req, res) => {
const bodyJson = JSON.parse(req.body)
// do something
}
I would just make a module similar to the json.js middleware module and just don't bother converting the buf data into anything else. Wrap it into a plain.js file, apply some decent "don't repeat yourself" refactoring, and submit a pull request to connect. Seems generally handy. However, note that while convenient, large enough request bodies will require streaming straight to disk at some point so you don't consume all the memory in your node server.
You may try this :
var expressApi= (req, res,params)=>{
console.log('req.body',params);
var body = '';
req.on('data', function (data) {
body += data;
});
req.on('end', function () {
res.write({status:200,message:'read data'});
});
}
You can parse every type to json via set type option
app.use(express.json({ type: ['text/*', '*/json'] }))

Resources