Handling text/plain in Express (via connect)? - node.js

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'] }))

Related

get angular form data value in node is empty

This is angular form data append part
const formData = new FormData();
formData.append('userId', this.myData.userId);
formData.append('file', this.file);
formData.append('action', 'upload');
And I send it to node server like this.
return this.http.post<any>(`${Server}/uploadPhoto`, formData);
But I can't get req.body value in node server
I used Express.
But req.body is empty.
This is node get part.
exports.uploadPhoto = async function(req, res) {
try {
console.log(req.body))
res.status(200).send(req.body)
} catch {
res.status(400).send()
}
Return value and node get value are all {}.
If I don't use formdata, node server works well.
I can get other values without formdata.
I think, it is due to angular header option or node accept type option, but I'm not sure.
This is node header part
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
res.header('Access-Control-Allow-Origin', 'http://localhost:4200');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Accept-Encoding');
For file upload with form data, you can try this library on your express API, inside have a lot of example how to implement this middleware.
https://github.com/expressjs/multer

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!

How to Allow Form-Data in NodeJS

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);
}

AngularJS $resource makes HTTP OPTIONS request instead of HTTP POST for $save method

I'm in the process of writing a simple library application to get ready for a larger project with AngularJS. After reading a lot online about using $resource to interact with a RESTful API, I decided that it would probably offer some time-saving and scaling benefits to implement it instead of using $http for each request. The problem is that for some reason (I'm no expert on CORS and the request is being sent cross-domain) when using the $save method my Node.js console shows:
OPTIONS /books 200 1ms - 161b
Using the query() method works fine - the Node console shows:
GET /books 200 1ms - 228b
I've been stuck for several hours at this point, trying variations on the below but it always ends up being an OPTIONS request instead of POST (which is what it should be according to the Angular documentation) for the $save method.
AngularJS Web App
app.js
var libraryApp = angular.module('libraryApp', ['ngResource', 'ngRoute', 'libraryControllers']);
libraryApp.factory('$book', ['$resource', function ($resource) {
return $resource('http://mywebserver\\:1337/books/:bookId', { bookId: '#bookId' });
}]);
controllers.js
var libraryControllers = angular.module('libraryControllers', []);
libraryControllers.controller('BookCtrl', ['$scope', '$book', function($scope, $book) {
...
$scope.addBook = function () {
var b = new $book;
b.isbn = "TEST";
b.description = "TEST";
b.price = 9.99;
b.$save();
};
}]);
Node.js with Express REST API
app.js
var express = require('express'),
books = require('./routes/books'),
http = require('http'),
path = require('path');
var app = express();
...
// enable cross-domain scripting
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", req.headers.origin);
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
// routing
app.get('/books', books.getAll);
app.get('/books/:isbn', books.get);
// This is what I want to fire with the $save method
app.post('/books', books.add);
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
./routes/books.js
...
exports.add = function(req, res) {
console.log("POST request received...");
console.log(req.body.isbn);
};
Tried putting this line in my config function delete $httpProvider.defaults.headers.common["X-Requested-With"]; but no change.
I'm no Angular/Node pro but right now I'm thinking that it's something to do with it being cross domain and, like I said, I'm no expert on CORS.
Thanks in advance.
I know it may be in bad taste to answer my own question but I figured out the problem a few days after posting this.
It all comes down to how browsers manage CORS. When making a cross-domain request in JavaScript that is not "simple" (i.e. a GET request - which explains why the query() function worked), the browser will automatically make a HTTP OPTIONS request to the specified URL/URI, called a "pre-flight" request or "promise". As long as the remote source returns a HTTP status code of 200 and relevant details about what it will accept in the response headers, then the browser will go ahead with the original JavaScript call.
Here's a brief jQuery example:
function makeRequest() {
// browser makes HTTP OPTIONS request to www.myotherwebsite.com/api/test
// and if it receives a HTTP status code of 200 and relevant details about
// what it will accept in HTTP headers, then it will make this POST request...
$.post( "www.myotherwebsite.com/api/test", function(data) {
alert(data);
});
// ...if not then it won't - it's that simple.
}
All I had to do was add the details of what the server will accept in the response headers:
// apply this rule to all requests accessing any URL/URI
app.all('*', function(req, res, next) {
// add details of what is allowed in HTTP request headers to the response headers
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Credentials', false);
res.header('Access-Control-Max-Age', '86400');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
// the next() function continues execution and will move onto the requested URL/URI
next();
});
And then insert these few lines before the Express routing to simply return a HTTP 200 status code for every OPTIONS request:
// fulfils pre-flight/promise request
app.options('*', function(req, res) {
res.send(200);
});
Hopefully this will help anyone who stumbles on this page suffering from the same problem.
I didn´t actually try this, but wouldn´t it be enough to tell the Ressource how to handle the $save request?
$resource('http://mywebserver\\:1337/books/:bookId', { bookId: '#bookId' }, {save: {method: 'POST'});

sails.js Getting a POST payload with text/plain content type

I'm developing a sails.js (node.js framework based on express) aplication, which is going great but ]I can't solve this detail...
I need to send POST requests cross domain from internet explorer 8 and 9. For that I'm forced to use xDomainRequest object, wich doesn't allow to set a Content type header.
So, when the request gets to the server the content type is "text/plain", which doesn't fire the bodyParser express middleware, so my req.body is an empty object and I can't see the payload I'm sending from the client.
For this I've tried two things with no luck:
First I wanted to set a header to the request in my first middleware, which is of course not available:
req.set("Content-type", "Apli.....
Then, I created a middleware that listens to req.on('data'), like this:
var data = "";
req.on('data', function(chunk){
data += chunk;
});
req.on('end', function(){
req.rawBody = data;
next();
});
But the data event never fires!
Does anyone know how can I access my raw payload, so I can parse it myself.
Thanks!
with newest version of Sails, using express is deprecated.
I needed to use a specific parser to get raw data from Stripe API.
Here is my code, maybe it will help somebody :
bodyParser: function(req, res, next) {
var skipper = require('skipper')();
var rawParser = require("body-parser").raw({type: "*/*"});
// Create and return the middleware function
return function(req, res, next) {
sails.log.debug(req.headers);
if (req.headers && req.headers['stripe-signature']) {
sails.log.info('request using raw parser middleware');
return rawParser(req, res, next);
}
// Otherwise use Skipper to parse the body
sails.log.info('request using skipper middleware');
return skipper(req, res, next);
};
},
I think in this case you're going to have to implement your own body parser, which you can set as sails.config.express.bodyParser or create a config/express.js file like:
var express = require('express');
module.exports.express = {
bodyParser: function(options) {
// Get default body parser from Express
var defaultBodyParser = express.bodyParser(options);
// Get function for consumung raw body, yum.
var getBody = require('raw-body');
return function (req, res, next) {
// If there's no content type, or it's text/plain, parse text
if (!req.headers['content-type'] ||
req.headers['content-type'].match('text/plain')) {
// flag as parsed
req._body = true;
// parse
getBody(req, {
limit: 100000, // something reasonable here
expected: req.headers['content-length']
}, function (err, buf) {
if (err) return next(err);
// Make string from buffer
buf = buf.toString('utf8').trim();
// Set body
req.body = buf.length ? {content: buf} : {}
// Continue
next();
});
}
// Otherwise try the default parsers
else return defaultBodyParser(req, res, next);
};
}
You'll have to npm install express and npm install raw-body. Note that this example uses the default Express body parser as a fallback, not the default Sails body parser which isn't exposed anywhere (and is mostly the same as Express anyway, sans the JSON retry).
maxime's answer is mostly correct. minor modification needed as follows:
bodyParser: function(req, res, next) {
var skipper = require('skipper')();
var rawParser = require("body-parser").raw({type: "*/*"});
// Create and return the middleware function
sails.log.debug(req.headers);
if (req.headers && req.headers['stripe-signature']) {
sails.log.info('request using raw parser middleware');
return rawParser(req, res, next);
}
// Otherwise use Skipper to parse the body
sails.log.info('request using skipper middleware');
return skipper(req, res, next);
},

Resources