This seems like it should be a fairly simple question, but I'm having a really hard time figuring out how to approach it.
I'm using Node.js + Express to build a web application, and I find the connect BodyParser that express exposes to be very useful in most cases. However, I would like to have more granular access to multipart form-data POSTS as they come - I need to pipe the input stream to another server, and want to avoid downloading the whole file first.
Because I'm using the Express BodyParser, however, all file uploads are parsed automatically and uploaded and available using "request.files" before they ever get to any of my functions.
Is there a way for me to disable the BodyParser for multipart formdata posts without disabling it for everything else?
If you need to use the functionality provided by express.bodyParser but you want to disable it for multipart/form-data, the trick is to not use express.bodyParser directly. express.bodyParser is a convenience method that wraps three other methods: express.json, express.urlencoded, and express.multipart.
So instead of saying
app.use(express.bodyParser())
you just need to say
app.use(express.json())
.use(express.urlencoded())
This gives you all the benefits of the bodyparser for most data while allowing you to handle formdata uploads independently.
Edit: json and urlencoded are now no longer bundled with Express. They are provided by the separate body-parser module and you now use them as follows:
bodyParser = require("body-parser")
app.use(bodyParser.json())
.use(bodyParser.urlencoded())
If the need for body parsing depends only on the route itself, the simplest thing is to use bodyParser as a route middleware function on only the routes that need it rather than using it app-wide:
var express=require('express');
var app=express.createServer();
app.post('/body', express.bodyParser(), function(req, res) {
res.send(typeof(req.body), {'Content-Type': 'text/plain'});
});
app.post('/nobody', function(req, res) {
res.send(typeof(req.body), {'Content-Type': 'text/plain'});
});
app.listen(2484);
When you type app.use(express.bodyParser()), almost each request will go through bodyParser functions (which one will be executed depends on Content-Type header).
By default, there are 3 headers supported (AFAIR). You could see sources to be sure. You can (re)define handlers for Content-Types with something like this:
var express = require('express');
var bodyParser = express.bodyParser;
// redefine handler for Content-Type: multipart/form-data
bodyParser.parse('multipart/form-data') = function(req, options, next) {
// parse request body your way; example of such action:
// https://github.com/senchalabs/connect/blob/master/lib/middleware/multipart.js
// for your needs it will probably be this:
next();
}
upd.
Things have changed in Express 3, so I'm sharing updated code from working project (should be app.useed before express.bodyParser()):
var connectUtils = require('express/node_modules/connect/lib/utils');
/**
* Parses body and puts it to `request.rawBody`.
* #param {Array|String} contentTypes Value(s) of Content-Type header for which
parser will be applied.
* #return {Function} Express Middleware
*/
module.exports = function(contentTypes) {
contentTypes = Array.isArray(contentTypes) ? contentTypes
: [contentTypes];
return function (req, res, next) {
if (req._body)
return next();
req.body = req.body || {};
if (!connectUtils.hasBody(req))
return next();
if (-1 === contentTypes.indexOf(req.header('content-type')))
return next();
req.setEncoding('utf8'); // Reconsider this line!
req._body = true; // Mark as parsed for other body parsers.
req.rawBody = '';
req.on('data', function (chunk) {
req.rawBody += chunk;
});
req.on('end', next);
};
};
And some pseudo-code, regarding original question:
function disableParserForContentType(req, res, next) {
if (req.contentType in options.contentTypes) {
req._body = true;
next();
}
}
Within Express 3, you can pass parameter to the bodyParser as {defer: true} - which in term defers multipart processing and exposes the Formidable form object as req.form. Meaning your code can be:
...
app.use(express.bodyParser({defer: true}));
...
// your upload handling request
app.post('/upload', function(req, res)) {
var incomingForm = req.form // it is Formidable form object
incomingForm.on('error', function(err){
console.log(error); //handle the error
})
incomingForm.on('fileBegin', function(name, file){
// do your things here when upload starts
})
incomingForm.on('end', function(){
// do stuff after file upload
});
// Main entry for parsing the files
// needed to start Formidables activity
incomingForm.parse(req, function(err, fields, files){
})
}
For more detailed formidable event handling refer to https://github.com/felixge/node-formidable
I've faced similar problems in 3.1.1 and found (not so pretty IMO) solution:
to disable bodyParser for multipart/form-data:
var bodyParser = express.bodyParser();
app.use(function(req,res,next){
if(req.get('content-type').indexOf('multipart/form-data') === 0)return next();
bodyParser(req,res,next);
});
and for parsing the content:
app.all('/:token?/:collection',function(req,res,next){
if(req.get('content-type').indexOf('multipart/form-data') !== 0)return next();
if(req.method != 'POST' && req.method != 'PUT')return next();
//...use your custom code here
});
for example I'm using node-multiparty where the custom code should look like this:
var form = new multiparty.Form();
form.on('file',function(name,file){
//...per file event handling
});
form.parse(req, function(err, fields, files) {
//...next();
});
With express v4, and body-parser v1.17 and above,
You can pass a function in the type of bodyParser.json.
body-parser will parse only those inputs where this function returns a truthy value.
app.use(bodyParser.json({
type: function(req) {
return req.get('content-type').indexOf('multipart/form-data') !== 0;
},
}));
In the above code,
the function returns a falsy value if the content-type is multipart/form-data.
So, it does not parse the data when the content-type is multipart/form-data.
throw this is before app.configure
delete express.bodyParser.parse['multipart/form-data'];
Related
My Code Returns Invalid Text When I Try To Do It.
app.post("/charge", (req, res) => {
console.log(req.body)
})
As the doc for req.body says:
req.body contains key-value pairs of data submitted in the request body. By default, it is undefined, and is populated when you use body-parsing middleware such as express.json() or express.urlencoded().
The following example shows how to use body-parsing middleware to populate req.body.
By default, the body of the request is not yet read from the incoming stream and therefore it is not yet parsed into req.body either. To get it read and parsed into req.body, you have to use some appropriate middleware that will do that for you (or you could do it manually yourself, but it's generally easier to use pre-written middleware that does the job for you).
Which middleware to use depends upon the type of the data in the body (urlEncoded data, JSON data or something else).
Here's the example from the doc:
var express = require('express')
var app = express()
app.use(express.json()) // for parsing application/json
app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
app.post('/profile', function (req, res, next) {
console.log(req.body)
res.json(req.body)
})
I have a node.js application which I use to interact with a REST API provided by another server. I would like to expose a web interface (html + css + javascript) using express.js in order to control the first application.
How can I let the browser talk to the server and let it make node.js actions like using http from that machine or writing to its filesystem? I tried using XMLHttpRequest, but HTTP requests are sent by my local PC instead of from my server.
The only solution I found is using XMLHttpRequest in the javascript of my web interface to invoke some middleware functions on my server, but I had some problems: when I make POST requests, I cannot read data from server. I used FormData and its append method to make the "body" of the POST request, then used body-parser in express to read that body, but it turns out to be always empty. Also tried changing the 'Content-Type' of the header.
Any suggestions? Any better solution than mine (I think it is not efficient)?
As pointed by Jonas, using node server as proxy would be the right approach.
I'm providing sample code for both frontend as well as backend app. Hope this helps you.
Frontend App Code
<html>
<head>
<script type="text/javascript">
function sendData(data) {
if (!data) {
// lets define some dummy data for testing
data = { somekey: "somevalue", anotherkey: "anothervalues" };
}
var XHR = new XMLHttpRequest();
var FD = new FormData();
// Push our data into our FormData object
for (name in data) {
FD.append(name, data[name]);
}
// Define what happens on successful data submission
XHR.addEventListener("load", function(event) {
alert("Yeah! Data sent and response loaded.");
alert(event.target.responseText);
});
// Define what happens in case of error
XHR.addEventListener("error", function(event) {
alert("Oops! Something went wrong.");
});
// Set up our request
XHR.open("POST", "http://path/to/your/nodejs/server/app");
// Send our FormData object; HTTP headers are set automatically
XHR.send(FD);
}
</script>
</head>
<body>
<button onclick="sendData()">Send Test Request to the Server</button>
</body>
</html>
Backend App code
const http = require('http');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.get('/', (req, res) => res.send('Yeah! Server is UP! Post some data'));
app.post('/', (req, res) => {
// You'll see the posted data in req.body, simply for testing purpose return it back to the calling user
res.json(req.body || {});
});
const server = http.createServer(app);
server.listen(3000);
server.on('error', console.error);
server.on('listening', () => console.log('Listening on 3000'));
process.on('exit', (code) => console.warn('Server terminated with code=' + code));
Please note that for this backend app to run, you must have installed following npm packages: express, body-parser
I am using Express and body-parser middleware to process incoming requests. On the front end, I have a form that's just a hidden input and a submit button (written in Pug):
form(notes="saveNotesForm" action=`/lessons/${lesson._id}/notes` method="POST" enctype="application/x-www-form-urlencoded")
input(type="hidden" id="hiddenNotes" name="hiddenNotes" alt="Notes Copy" value="test").notesHidden
input(type="submit" name="saveNotes" alt="Save Notes" value="Save")
On the backend, I have the Express app using body-parser:
const bodyParser = require('body-parser');
// ...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
And the route processing the incoming request:
router.post('/lessons/:lessonID/notes', lessonController.updateNotes);
// ... and in lessonController:
exports.updateNotes = async (req, res) => {
console.log('updateNotes request received');
console.log(req.body);
res.status(200).json({ status: 'success' });
}
When I try to use req.body in updateNotes, the body is an empty object, but should at least have the property hiddenNotes with the value "test". Please comment if you have any questions or think you see the problem!
[UPDATED]
This was a silly mistake, I forgot I had written a separate event handler for when this form gets submitted - it just took me posting on SO before I went through all of my code :) The event handler uses axios and looks like this:
const SaveNotesButton = $('form.saveNotes');
SaveNotesButton.on('submit', ajaxSaveNotes);
function ajaxSaveNotes(e) {
e.preventDefault();
axios
.post(this.action)
.then(res => {
console.log(res.data);
})
.catch(console.error);
}
Unfortunately, when making posts with axios, you need to include the data in an object like this, or else it won't be included in the request:
const SaveNotesButton = $('form.saveNotes');
SaveNotesButton.on('submit', ajaxSaveNotes);
function ajaxSaveNotes(e) {
e.preventDefault();
axios
.post(this.action, { notes: this.children.hiddenNotes.value }) // Data added here
.then(res => {
console.log(res.data);
})
.catch(console.error);
}
Have to tried querying your end point using Postman or something similar?
There could be one possible explanation for why your req.body is undefined. Are you sure your app.use(bodyParser.json()) and app.use(bodyParser.url... are written before your app.use('***', router) lines? If you put the body parser middleware AFTER your router middleware it essentially won't get called because middleware are called in the order they are put in app.use. So you should place your bodyParser before all your router logic. BodyParser middleware then calls all the other middleware (with the req.body populated) that come after it including all your router level middleware.
Hope it helps!
Not sure how you are defining your routes but I would expect a post route to look like the following:
const express = require('express');
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.post('/lessons/:lessonID/notes', (req, res) => {
// now pass the req object to your function
lessonController.updateNotes(req);
});
You need to pass the req object to your function to be able to access req.body...
I'm attempting to store an object that my user clicks on in my server so that when the page changes, all the information from that object can be displayed fully in a profile page.
I'm unfamiliar with Angular $http but I've tried to write a call that will POST to the server, unfortunately when I scan through the req object in VScode I can't find where the object I sent is contained, so I can send it on to my function.
Controller function:
$scope.storeProfile = function(child){
$http.post('/storeTempProfile', child)
.then(function(response) {
window.location.href = 'DemoPage.html';
});
}
server.js:
app.post('/storeTempProfile', function (req, res) {
profileStorage.storeProfile(req);
});
does my app.post look right? And what property of req do I need to use the dot operator on to access my object? I can't seem to find the object data anywhere in req and that makes me thing there's something wrong with how I wrote app.post
It looks like you are using express. So in that case, you want to access the object on req.body, but this will require you use body-parser. The example on their homepage:
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// create application/json parser
var jsonParser = bodyParser.json()
// POST /api/users gets JSON bodies
app.post('/api/users', jsonParser, function (req, res) {
if (!req.body) return res.sendStatus(400)
// create user in req.body
})
You will notice in this example that they pass the json parser into the route itself. This is only necessary if you want to have different parsers for different routes. Usually you just want to set it to all routes, which you can do by using app.use(bodyParser.json()).
I am working with node/express/passport/ looking at code that attempts to use a request like:
req._parsedUrl.pathname;
I cannot figure out where this variable is coming from. Is this a canonical variable name that is set in a common .js library? It doesn't seem exposed in any headers.
req._parsedUrl is created by the parseurl library which is used by Express' Router when handling an incoming request.
The Router doesn't actually intend to create req._parsedUrl. Instead parseurl creates the variable as a form of optimization through caching.
If you want to use req._parsedUrl.pathname do the following instead in order to ensure that your server doesn't crash if req._parsedUrl is missing:
var parseUrl = require('parseurl');
function yourMiddleware(req, res, next) {
var pathname = parseUrl(req).pathname;
// Do your thing with pathname
}
parseurl will return req._parsedUrl if it already exists or if not it does the parsing for the first time. Now you get the pathname in a save way while still not parsing the url more than once.
You can write a middleware to handle then set properties for req.
var myMiddleWare = function () {
return function (req, res, next) {
req._parsedUrl = 'SOME_THING';
next()
}
};
app.get('/', myMiddleWare, function (req, res) {
console.log(req._parsedUrl); // SOME_THING
res.end();
})
Express middleware document in here