How to wrap multiple middleware functions into one? - javascript

I have a number of middleware functions similar to the following:
function validate(req, res, next) {
req.validationError = new Error('invalid');
}
function checkValid(req, res, next) {
if (req.validationError) {
next(req.validationError);
} else {
next();
}
}
function respond() {
res.json({result: 'success'});
}
Is there a way to wrap them into one function? So I'd do something like:
function respondIfValid(req, res, next) {
// Evoke the following middleware:
// validate
// checkValid
// respond
}
app.use('/', respondIfValid);
Instead of:
app.use('/', validate, checkValid, respond);

try with following code
app.use('/', [validate, checkValid,respond]);
OR
var middleware = [validate, checkValid,respond];
app.use('/', middleware );
Need to placed all function in that series as your requirement of execution.
Thanks

Related

Express.js: Match route in catch all

Can one check a express.js route against multiple patterns? Consider the catch all * route below.req.route is matched to * here. I'd like to check the route against a few special scenarios within the same callback ~ NOT inside another all or use middleware.
app.all('*', (req, res, next) => {
// How do I check if route is a special case like below
if(req.route in ['/foo/:param', '/bar/:param']){}
})
I'm not sure why you're dismissing separate .all routes for this, because it seems to me to be the best way of performing these checks:
app.all('/foo/:param', (req, res, next) => {
req.isFoo = true;
next();
});
app.all('/bar/:param', (req, res, next) => {
req.isBar = true;
next();
});
app.all('*', (req, res, next) => {
if (req.isFoo || req.isBar) { ... }
})
Or, analogous to Chris's answer, have one route to match both:
app.all([ '/foo/:param', '/bar/:param' ], (req, res, next) => {
req.isSpecial = true;
next();
});
So you should not try to use the wildcard to capture everything than look for specific values. Instead, create an endpoint that looks for these specific values and then use another route for the capture all wildcard.
app.get(['/test', '/another_value'], (req, res, next) => {
})

Use an array of middlewares at express.js

I'm trying to use an array of middlewares. Well, more like a combination of function names and arrays.
Instead of having:
router.post('/editPassword', validate, changePassword, sendConfirmation);
I would like to have something like:
router.post('/editPassword', validate, [changePassword, sendConfirmation] );
That would look like:
router.post('/editPassword', validate, doAction );
Where doAction would be an array like this:
var doAction = [
//equivalent of changePassword
function(req, res, next){
//whatever
next();
},
//equivalent to the previous sendConfirmation
function(req, res, next){
//whatever
}
]
But it seems it is failing and going back to the validate step after the next() within the first function in doAction.
I'm looking for a way to simplify the middleware chaining including some middleware steps under the same name.
Latest version of Express can handle this:
function logOriginalUrl (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}
function logMethod (req, res, next) {
console.log('Request Type:', req.method)
next()
}
var logStuff = [logOriginalUrl, logMethod]
app.get('/user/:id', logStuff, function (req, res, next) {
res.send('User Info')
})
You can review more from this link
I assume the reason you wanted it to look that way is not only for it to appear presentable, but also to be able to reuse the other middleware. In that case, you can create a middleware which runs all other middlewares to do the check for you, and only calls the next function if all validations succeed.
var express = require('express');
var app = express();
function middleware1(req, res, next) {
if(req.query.num >= 1) {
next();
} else {
res.json({message: "failed validation 1"});
}
}
function middleware2(req, res, next) {
if(req.query.num >= 2) {
next();
} else {
res.json({message: "failed validation 2"});
}
}
function middleware3(req, res, next) {
if(req.query.num >= 3) {
next();
} else {
res.json({message: "failed validation 3"});
}
}
function combination(req, res, next) {
middleware1(req, res, function () {
middleware2(req, res, function () {
middleware3(req, res, function () {
next();
})
})
})
}
app.get('/', combination, function (req, res) {
res.send('Passed All Validation!');
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
You can test this app by running it then viewing http://localhost:3000/?num=3, changing the value 3 to a lower number, or removing the num parameter.
I'm not sure if this is the proper way to do it, but this is how I've handled my other projects. Let me know what you think.
note: see comments for use case. #robertklep may have a better solution depending on how you want to use middlewares
Just search a little more ^^ : Less ugly and more understandable than previous answer
https://github.com/blakeembrey/compose-middleware
Be careful that you're not doing (the equivalent of) this in your validate middleware:
function middleware(req, res, next) {
if (someCondition) {
console.log('some condition is true');
next();
}
console.log('some condition is false');
res.status(400).end();
}
The intention here is that after calling next the rest of the code isn't executed, but it will. There's nothing really special about next, so when you call it, after it returns the middleware code continues to run (causing both "some condition is true" and "some condition is false" to be logged).
That's why you often see this:
if (someCondition) {
console.log('some condition is true');
return next();
// Or, alternatively:
// next();
// return;
}
The return causes the middleware function to return after calling next, so the rest of the code in the function won't be executed.
This functionality is already built into express as an array or middleware:
let combined = express.Router()
.use(
[
middleware1,
middleware2,
middleware3,
],
);
let combined = express.Router()
.use(
middleware1,
middleware2,
middleware3,
);
Full Example
"use strict";
let Http = require("http");
let Express = require("express");
let server = Express();
let app = Express.Router();
let combined = Express.Router();
combined.use(
function (req, res, next) {
console.log("huzzah!");
next();
},
function (req, res, next) {
res.json({ success: true });
}
);
function middleware0(req, res, next) {
console.log('ground zero');
next();
}
app.get("/combined", middleware0, combined);
server.use("/", app);
Http.createServer(server).listen(3000);

Any way to conditionally skip middleware with next() in express?

So for example-
//Functions
var validationCheck = function(req, res, next){
if(req.session.userId && req.session.token)
next('validationCatch') //Is this possible to skip middleware if not needed?
else
next()
}
var getUserId = function(req, res, next){
req.session.userId = DB.getUserId
next()
}
var getToken = function(req, res, next){
req.session.token = DB.getToken
next()
}
//a catcher function to direct to next function depending on route
var validationCatch = function(req, res, next){
next()
}
//Routes
app.get(api + '/feature1', validationCheck, getUserId, getToken, validationCatch, feature1)
app.get(api + '/feature2', validationCheck, getUserId, getToken, validationCatch, feature2)
My goal is to skip unnecessary middleware if information is already cached from previous routes. So in above example, feature2 doesnt have to enter getUserId and getToken if feature1 has already been called, thus making response faster.
If not possible, please state a more efficient architecture to handle repetitive validation processes. Thanks
Instead of register the callbacks directly, just call them in validationCheck if needed.
Here is a simple example with getUserId:
var validationCheck = function(req, res, next){
if(req.session.userId)
next()
else {
getUserId(req, res, function(err){
next(err)
})
}
}

Using middleware with app.use

what is the difference between:
function setLocale(req, res, next) {
req.params.locale = req.params.locale || 'pt';
res.cookie('locale', req.params.locale);
req.i18n.setLocale(req.params.locale);
console.log(req.params.locale);
next();
}
app.get('/:locale?', setLocale, function(req, res) {
res.render("index");
});
And this:
app.use(setLocale);
function setLocale(req, res, next) {
req.params.locale = req.params.locale || 'pt';
res.cookie('locale', req.params.locale);
req.i18n.setLocale(req.params.locale);
console.log(req.params.locale);
next();
}
app.get('/:locale?', function(req, res) {
res.render("index");
});
??
Only the first is working, if i try to use app.use, the code will broke cause req.params.locale will be undefined.
The problem is that when you use app.use(setLocale); all you calls will be passed that function. Even if you call the url / that code will run and then param will be undefined.
The fisrt option you have (app.get('/:locale?', setLocale,) you use that function only when that url matches and there is a locale that you can use inside the function.
app.use will add the middleware to the stack and use it before each request is processed, always, regardless of route, method etc.
In the first example the middleware is added as a callback function to that route only, as app.get accepts multiple callbacks, and calling next moves to the next callback etc
app.get('/', function(req, res, next) {
next(); // move to next
}, function(req, res, next) {
// continues here when next() is called in previous callback etc.
});
this means that in the first example the setLocale function is only called when the route matches /:locale?, while in the second example using app.use will always call the setLocale function before the routes callback is executed.
Unfortunately req.params is not available in app.use as it depends on the router and is added later, so you're probably stuck with including the function as a callback to every route, and you could probably do that with app.all('*')
function setLocale(req, res, next) {
req.params.locale = req.params.locale || 'pt';
res.cookie('locale', req.params.locale);
req.i18n.setLocale(req.params.locale);
next();
}
app.all('*', setLocale);
app.get('/:locale?', function(req, res) {
res.render("index");
});

exit from chain of route specific middleware in express/ nodejs

I have a chain of "route specific middleware" for this route, like so:
var express = require('express');
var server = express();
var mw1 = function(req, resp, next) {
//do stuff
if (success) {
next();
} else {
req.connection.destroy(); //without calling next()
}
};
var mw2 = function(req, resp, next) {
//do stuff
if (success) {
next();
} else {
req.connection.destroy(); //without calling next()
}
};
server.post('/some/path', [mw1, mw2], function(req, resp) {
//write response
});
[mw1, mw2] are the middleware specific to the route /some/path.
This is different from server-wide middleware like this:
server.use(mw1);
server.use(mw2);
Where it applies to all routes defined.
Now my issue is that I want to exit from the chain. I.e. if success is false in mw1, I do not wish for mw2 to be called. If success is false in mw2, I do not without for the route function to be called. Presently, both mw1 and mw2 appear to be getting called whether or not next() is called - and I do not know why.
How can I go about doing this?
You can call next( 'route' ), as said on the express api reference, application routing section:
Multiple callbacks may be given, all are treated equally, and behave
just like middleware, with the one exception that these callbacks may
invoke next('route') to bypass the remaining route callback(s).
Example
var express = require('express')
, app = express()
;
// keep an eye on the function names
app.post( '/some/path', middleware1, middleware2, function route1( req, res, next ) {
// write response
});
app.all( '*', function route2( req, res, next ) {
// do something like handle a 404 request
});
app.use(function errorHandler( err, req, res, next ) {
// handle error
});
function middleware1( req, res, next ) {
// ...
if ( !success ) {
// bypasses middleware2 and route1, route2 will be called
return next( 'route' );
}
// calls middleware2
next();
}
// intentionally similar to middleware1 to keep things clear
function middleware2( req, res, next ) {
if ( !success ) {
// bypasses route1 and route2
// errorHandler will be called with the error
return next( Error( 'middleware 2 failed' ) );
}
// calls route1
next();
}
A little more tinkering yielded the answer:
var express = require('express');
var server = express();
var mw1 = function(req, resp, next) {
//do stuff
if (success) {
next();
} else {
resp.send(406, 'Invalid because of this');
req.connection.destroy(); //without calling next()
}
};
var mw2 = function(req, resp, next) {
//do stuff
if (success) {
next();
} else {
resp.send(406, 'Invalid because of that');
req.connection.destroy(); //without calling next()
}
};
server.post('/some/path', [mw1, mw2], function(req, resp) {
//write response
});
The trick was send a response: resp.send(406, 'Invalid because of this');
Just prior to destroying the connection: req.connection.destroy();
In fact not destroying the connection, I found to also work, in the general case.
(But was required in my specific case, and is out of the scope of this question.)
If the response has already been sent, then express does not automatically call next() for you, as it appeared to do otherwise.
I was under the impression that if you neither call next() nor send a response in a route handling function, express just hangs. Also FWIW I haven't used an array, mine looks like server.post('/some/path', mw1, mw2, function(req, resp) {...
Anyway. One alternative might be to restructure your code so you only have a single handling function. Do you have a good reason for mw1 and mw2 being middleware instead of regular async functions your handler calls?
var express = require('express');
var server = express();
var mw1 = function(req, res, callback) {
// do stuff with req/res if necessary but don't send a response
if (success) {
callback(null);
} else {
callback('Error');
}
};
var mw2 = function(req, res, callback) {
//do other stuff but don't send a response
if (success) {
callback(null);
} else {
callback('Error');
}
};
function mwBoth(req, res){
mw1(req, res, function(err){
if(err){ return res.send(500) };
mw2(req, res, function(err){
if(err){ return res.send(500) };
// neither had an error
res.redirect('/some/other/path');
});
});
};
server.post('/some/path', mwBoth);

Categories