Express: rawBody / buffer on a single route - javascript

I need to grab the rawBody (buffer) for a POST request, but I only need to do this on a single route in my application.
Currently, I'm using the following in my main app.js:
app.use(bodyParser.json({ verify: function(req, res, buf) { req.rawBody = buf; }}));
This works, but it is sending the buffer for every route. This is undesirable because it uses twice the RAM for every request in my app.
How can I instead obtain the rawBody on the single route I need it for?

Typically, you'd create that single route and inline the middleware:
app.post('/route', bodyParser.json(...), function(req, res) {
...
});
Followed by the "normal" setup:
app.use(bodyParser.json());
app.post('/another-route', function(req, res) {
...
});
Instead of abusing bodyParser.json(), you could also consider using bodyParser.raw().

If its trivial to refer to the specific route then this would work.This is a very simplistic representation of the idea:
var routeIWantToBuffer = "someroute";
app.use(bodyParser.json(
{
verify: function(req, res, buf) {
if(req.route === routeIWantToBuffer) req.rawBody = buf;
}
}
));
Usually route/use functions also have a next argument which is used to continue the chain. I'm not saying this is wrong, but I don't know what the rest of your code looks like. But I would expect it to look like this:
var routeIWantToBuffer = "someroute";
app.use(bodyParser.json(
{
verify: function(req, res, buf, next) {
if(req.route === routeIWantToBuffer) req.rawBody = buf;
next();
}
}
));

Related

How to automate next() call in every route function? (express.js)

Hi I am facing the problem that I need to log each incomming request and the associated responses in my database. My current solution looks like the following:
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.status(200).send(req.allCustomers);
next(); // <- this is my personal problem
});
./middleware/logging.js
module.exports = function (req, res, next) {
db.query(
`INSERT INTO logging SET ?`,
{
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(req.response)
}
);
}
routes declaration
module.exports = function(app) {
app.use(express.json());
app.use('/api/customers', customers); // <- ROUTE ./routes/customer.js
app.use(logging); // <- MIDDLEWARE ./middleware/logging.js
}
I already mentioned my problem in my first piece of code. It is really repetitive to call next() in every route manually and I would like to avoid this. I already tried to load the middleware before all routes, call next() in the middleware function and execute my db query afterwards but I do not have the response at this point because of the async functionality.
Is there any way to handle this situation or will I need keep calling next() at the end of each route function?
If you don't want to call next() from your routes, you cannot have middleware run after them. It needs to be placed before. But can you get the response inside a middleware that runs before the route? The answer is yes!
It may be a little hacky, but since your route uses res.send(), you can use that to your advantage. By running before your route, your middleware can hijack that res.send function, to make it do other stuff.
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.send(req.allCustomers); // We'll hijack this
});
./middleware/logging.js
module.exports = function (shouldBeLoggedFunc) {
return function (req, res, next) {
if (shouldBeLoggedFunc(req)) {
// Store the original send method
const _send = res.send;
// Override it
res.send = function (body) {
// Reset it
res.send = _send;
// Actually send the response
res.send(body);
// Log it (console.log for the demo)
console.log(`INSERT INTO logging SET ?`, {
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(body)
});
};
}
next();
};
};
routes declaration
function shouldBeLogged(req) {
// Here, check the route and method and decide whether you want to log it
console.log(req.method, req.path); // e.g. GET /api/customers
return true;
}
module.exports = function(app) {
app.use(express.json());
app.use(logging(shouldBeLogged)); // <- Place this before your routes
app.use('/api/customers', customers);
};
when you use express.Router class like you already did and then use this code
app.use('/api/customers', customers);
you don't have to write 'next()' inside callback function in router.get .
there is an example
create a router file named birds.js in the app directory, with the following content:
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
Then, load the router module in the app:
var birds = require('./birds')
// ...
app.use('/birds', birds)

Express router matching params

Assuming I have two routes one with params, one without:
/foo?bar
/foo
I want to use two different handlers for these two routes. I know I can do something like this.
app.use('/foo', (req, res) => {
if (req.params.foo !== undefined) {
// do something
} else {
// do something else
}
})
But, it would make the code harder to read. Is there a way to match a route that has a parameter? I would like to manage this situation:
app.use('/foo', x);
app.use('/foo?bar', y);
As far as I know, queries can not be filtered on use handler.
Instead, I made out with the very similar situation by using next.
app.use('/foo', (req, res, next) => {
if (req.query.foo !== undefined) return next();
//if foo is undefined, it will look for other matching route which will probably the next '/foo' route
/* things to do with foo */
});
app.use('/foo', (req, res) => {
//things to without foo
});
https://expressjs.com/en/guide/using-middleware.html
this document may also help you
How about this?
const express = require('express');
const app = express();
// curl -X GET http://localhost:3000/foo
app.get('/foo', function (req, res, next) {
res.send('This is foo');
});
// curl -X GET http://localhost:3000/foo/bar
app.get('/foo/:?bar', function (req, res, next) {
res.send('This is foo with bar');
});
app.listen(3000);

How to clear middleware loading to make dynamic route loading?

I don't know if it's possible, but I need to loading dinamically route files in middleware, according to a conditional.
Here we have the code that do well job in the first request, but seems that in next request, he enters inside right place of conditional but not use right file, seems that he uses cache file or something of previous request...
let routesApp = require('./routes-app');
let routesWeb = require('./routes-web');
app.use((req, res, next) => {
const regex = new RegExp(pattern, 'i')
if (regex.test(req.headers['agent-type'])) {
app.use('/', routesWeb)
} else {
app.use('/', routesApp)
}
return next()
})
How do I make this works?
When you call app.use the middleware passed to it gets registered for that path and subsequent requests will be handled by that .
One way to handle what you are doing is define a middleware which conditionally delegates to your web or mobile middleware as needed.
Sample Code:
let routesApp = require('./routes-app');
let routesWeb = require('./routes-web');
app.use('/', (req, res, next) => {
const regex = new RegExp(pattern, 'i')
if (regex.test(req.headers['agent-type'])) {
routesWeb(req, res, next);
} else {
routesApp(req, res, next);
}
})

How to display message using connect-flash and express-messages on .dust file on Node

I'm using Nodejs and Expressjs and Kraken, I need to display message when added a product on index but I tried many time for to config but messages still not appear as I expect. Here is my config.js:
var flash = require('connect-flash');
app = module.exports = express();
app.use(kraken(options));
//flash
app.use(flash());
app.use(function (req, res, next) {
res.locals.messages = require('express-messages')(req, res);
next();
});
My controller :
router.post('/somePath', function (req, res) {
//something to do to add
res.flash('messages','Add success!!')
res.render('path/index');
});
My index.dust file:
`{>"layouts/master" /}
{<body}
{messages|s}
// body goes here
{/body}
`
You're pretty close to the answer.
This line
res.locals.messages = require('express-messages')(req, res);
Stores a function in messages that outputs the flash messages as an html fragment.
res.locals is merged by express with the models that are used to render your template.
Now you just need a way to invoke this function from within the dust template.
Doing this:
{messages|s}
Doesn't actually invoke the function. You need to call it as if it were a context helper:
{#messages /}
You'll have one last hurdle to clear.
The function signature that express-messages expects, is different from what dust provides, so you'll have to wrap it within a helper function (in your server.js file):
app.use(flash());
app.use(function (req, res, next) {
var messages = require('express-messages')(req, res);
res.locals.messages = function (chunk, context, bodies, params) {
return chunk.write(messages());
};
next();
});

Escaping /: routes in Express in order to load js and css

How can I escape routes in Express and Node.js when using the /: notation? Here's what I'm doing:
app.get('/:route1/:route2', function(req, res){
var route1 = req.params.route1;
var route2 = req.params.route2;
MongoClient.connect(MongoUrl, function(err, db) {
if(err) throw err;
db.collection(route1)
.findOne({'_id' : new ObjectID(route2)},
function(err, doc){
res.send(doc);
});
});;
But by doing that, it won't load the js or css. I've tried if statements to no avail:
if(req.params.route1 !== 'javascripts'){//then do something}
First, without any knowledge of what you're doing or how your app is structured, I can't say for sure, but:
What you're doing (routes like /:var1/:var2) is a code smell to me. If api.function looks something like
if (req.params.var1 == 'foo') {
// do stuff
} else if (req.params.var1 == 'bar') {
// do other stuff
}
...that's not really the correct way to structure an Express application. In general, it should look more like
app.get('/foo/:var2', function(req, res) {
// do stuff
});
app.get('/bar/:var2', function(req, res) {
// do other stuff
});
That being said, if you really need to have your route handler ignore a certain value, you could just call next:
app.get('/:route1/:route2', function(req, res, next) {
if (req.params.route1 == 'javascripts') next();
else {
// do something
}
});
Are you using the connect static middleware?
app.use(express.static(__dirname + 'public'))
This says "any request to a file in the /public folder, serve it as a static file".
Make sure this appears above any app.get routes, so it will be used first.
you should move your static middleware above your route
app.use(express.static(path.join(__dirname, 'public')));
app.use(app.router);
app.get('/:route1/:route2', api.function);

Categories

Resources