I am building a RESTful API using expressJS, In my controller I have several functions like Chall_1, Chall_2,...
exports.validateChall_1 = function(res) {
//logic
res.json(1);
};
exports.validateChall_2 = function(res) {
res.json(2);
};
exports.validateChall_3 = function(res) {
res.json(3);
};
in my router.js I want to route the URL to a specific function based on challId which is a parameter in url
'use strict';
module.exports = function(app) {
var jsvalidator = require('../controllers/jsvalidatorController');
app.route('/chall/:challId')
.get(/*jsvalidator.validateChall_ + req.params.challId*/);
};
Is it possible to route directly to a specific function based on challId parameter?
you could do something like
app.route('/chall/:challId')
.get(function (req, res, next) {
switch (req.params.challId) {
case 1:
ctrl.validate_chall1(req, res, next);
break;
case 2:
ctrl.validate_chall2();
break;
default:
next() //it should continue to 404 route
break;
}
});
but I think doing this is better to keep the routes clean
app.route('/chall/challId/validate')
.get(ctrl.validate)
//in ctrl
function validate(req, res, next){
if(req.params.challId === 1)
validate_ctrl1()
//etc
}
Hey I'm using basic auth for Node.JS to secure a route. I'm pretty new to Node.JS and don't understand what the next function does in this case. What I'm trying to do is to secure a the route: /admin/
Note: This is a project for learning purposes so the login part is not too serious and won't be used live.
authentication.js
var basicAuth = require('basic-auth');
exports.BasicAuthentication = function(request, response, next) {
function unauthorized(response) {
response.set('WWW-Authenticate', 'Basic realm=Authorization Required');
return response.send(401);
};
var user = basicAuth(request);
if (!user || !user.name || !user.pass) {
return unauthorized(response);
};
if (user.name === 'name' && user.pass === 'pass') {
return next();
} else {
return unauthorized(response);
};
};
and app.js where I imported the module authentication:
app.get('/admin/', authentication.BasicAuthentication, function(req, res){
console.log("hi u need to login");
});
So what I want to do is to route the user further if the authentication goes through.
Thanks in advance!
Try:
app.get('/admin/', authentication.BasicAuthentication);
app.get('/admin/', function(req, res) {});
This function is known as a middleware:
var basicAuth = require('basic-auth');
exports.BasicAuthentication = function(request, response, next) {
function unauthorized(response) {
response.set('WWW-Authenticate', 'Basic realm=Authorization Required');
return response.send(401);
};
var user = basicAuth(request);
if (!user || !user.name || !user.pass) {
return unauthorized(response);
};
if (user.name === 'name' && user.pass === 'pass') {
return next();
} else {
return unauthorized(response);
};
};
middleware is a function that you can define for various purposes:
using middleware
writing a middleware
In a simple way is a function that runs before performing another action, one general purpose is to protect certain routes for unauthorized access.
You can protect private routes calling then authentication.BasicAuthentication before function(req, res) {}
Some example:
app.get('/user-profile/', authentication.BasicAuthentication, function(req, res){
//private info
});
app.get('/foo/', function(req, res){
//public info
});
I have a working node.js restify server configured to work as a static webserver. Here is the relevant code;
var server = restify.createServer({
name: 'myapp',
version: '1.0.0'
});
var static_webserver = function (app) {
app.get(/.*/, restify.serveStatic({
'directory': 'static', //static html files stored in ../static folder
'default': 'index.html'
}));
} //var static_server = function (app)
static_webserver(server);
After I enable HTTP Basic authentication to have better security for the other REST APIs, the static webserver stopped working.
This is the code for enabling HTTP Basic authentication.
server.use(restify.authorizationParser());
function verifyAuthorizedUser(req, res, next)
{
var users;
users = {
foo: {
id: 1,
password: 'bar'
}
};
if (req.username == 'anonymous' || !users[req.username] || req.authorization.basic.password !== users[req.username].password) {
// Respond with { code: 'NotAuthorized', message: '' }
next(new restify.NotAuthorizedError());
} else {
next();
}
next();
}//function verifyAuthorizedUser(req, res, next)
server.use(verifyAuthorizedUser);
It seems that after enabling authentication, no web browser was able to visit the static html webpages because authentication is required. How can I disable authentication for the static webserver but enable authentication for the other REST APIs?
If your nodejs is up to date enough to support string.startsWith():
function verifyAuthorizedUser(req, res, next) {
var path = req.path();
// check if the path starts with /static/
if (path.startsWith('/static/')) {
return next();
}
var users;
users = {
foo: {
id: 1,
password: 'bar'
}
};
if (req.username == 'anonymous' || !users[req.username] || req.authorization.basic.password !== users[req.username].password) {
// Respond with { code: 'NotAuthorized', message: '' }
next(new restify.NotAuthorizedError());
} else {
next();
}
next();
}
If you don't have startsWith(), a good old regex will do:
if (path.match(/\/static\/.*/)) {
return next();
}
I have a question regarding building a HTTP service with express that requires to run some pre-checks before running the actual function (and if one of the pre-checks fails, not to run the function).
For example, first check if the request is authorized, then check the permissions, and only if both pass run the actual function.
I can think of two ways to do that with express, either by writing both authorization and permissions as middlewares, or using callbacks.
Which approach is the best practice for express? Using middlewares looks more elegant, but is it better? What are the pros and cons for both solutions?
Using callbacks will look like this:
router.get( '/myResource',, function(req, res){
autorizationCB(function(err, data){
if (err) {
return res.status(401).json({message: err.message});
}
permissionsCB(function (err, data) {
if (err) {
return res.status(403).json({message: err.message});
}
// actual resource code...
});
});
});
function authorizationCB(callback) {
// check authorization
...
...
if (authorized) {
return callback(null, data);
} else {
return callback({message: 'some authorization error msg'});
}
}
function permissionsCB(callback) {
// check permissions
...
...
if (permitted) {
return callback(null, data);
} else {
return callback({message: 'some permissions error msg'});
}
}
Middlewares would look like this:
router.get('/myResource',
authurizationMW,
permissionsMw,
function(req, res){
// actual resource code...
});
function authorizationMW(req, res, next) {
//check if authorized
...
...
if (authorized) {
next();
} else {
res.status(401).json({message: 'not authorized'});
}
}
function permissionsMW(req, res, next) {
//check if permitted
...
...
if (permitted) {
next();
} else {
res.status(403).json({message: 'no permissions'});
}
}
Actually middlewares are exactly for these purposes, and using them there is no need to add required middlewares to every single route, you can add them to the router and they will only get called for routes under the router:
app.use('/user', authorizationMW, permissionsMW, require('./routes/user'));
I haven’t seen your mentioned callback strategy anywhere. Downsides, your code won't be readable, there are many nested levels and you must repeat all of these codes to each route on the router!
Middlewares are more reusable.
I would actually do something like this for example on how to better reuse:
var secureRouter = ...
secureRouter.use(authorizationMW);
secureRouter.use(permissionsMW);
var appRouter = ....
secureRouter.use(appRouter);
//all routes are protected....
appRouter.get('/myResource',
function(req, res){
// actual resource code...
});
);
appRouter.get('/myResource2',
function(req, res){
// actual resource code...
});
);
//.. so on....
I am currently working on a project to develop an API manager to control an existing API.
It contains a list of "before" and "after" middlewares, which are used to do things like security checking and logging. And a "service" middleware to do http request to the existing API. But the problem is that I want to make the order the middleware being executed to be dynamic, meaning that I could load some configuration file to change the order the middleaware get executed every time the request comes in.
here is my previous code:
'use strict';
// Loading the express library
var express = require('express');
var app = express();
var service = require('./routes/index');
// Testing configurable middleware
var confirguration = {
before1: {
priority: 100,
enable: true
},
before2: {
priority: 80,
enable: true
},
service: {
priority: 50,
enable: true
},
after1: {
priority: 30,
enable: true
},
after2: {
priority: 10,
enable: true
}
}
var before1 = require('./example_middleware/before1');
var before2 = require('./example_middleware/before2');
var after1 = require('./example_middleware/after1');
var after2 = require('./example_middleware/after2');
// Fake request to simulate the /service
var fakeRequest = require('./example_middleware/fake_request');
// Function to sort the order of the middleware to be executed
var sortConfig = function(confirguration){
var sortable = [];
for (var middleware in confirguration)
// To make middlewares configurable
if (confirguration[middleware]['enable'] == true){
sortable.push([middleware, confirguration[middleware]['priority']]);
}
sortable.sort(function(a, b) {return b[1] - a[1]});
return sortable;
}
// var sortedConfig = [];
var middlewareSet = new Array();
app.use('/test', function(request, response, next){
var middleware;
var sortedConfig = sortConfig(confirguration);
for (var i in sortedConfig){
switch(sortedConfig[i][0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
// console.log(sortedConfig[i][0]);
// Execute the middleware in expected order
middlewareSet.push(middleware);
}
// request.sortedConfig = sortedConfig;
console.log(middlewareSet);
console.log('middleware list sorted');
next();
});
app.use('/test', middlewareSet);
But I keep getting the same error message coming from the app.use() at the last line:
app.use() requires middleware functions
It works if I use:
app.use('/test', [before1, before2, fakeRequest, after1, after2]);
But it's not dynamic though, what did I misunderstand? There must be a way to do this in express.js.
Thanks in advance.
EDIT:
I modified my code according to Ryan's answer, here is the code:
var async = require('async');
app.use('/test', configurableMiddleWare);
function configurableMiddleWare(req, res, next) {
var operations = [];
var middleware;
var sortedConfig = sortConfig(confirguration);
// push each middleware you want to run
sortedConfig.forEach(function(fn) {
switch(fn[0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
operations.push(middleware); // could use fn.bind(null, req, res) to pass in vars
});
console.log('middleware list sorted');
// now actually invoke the middleware in series
async.series(operations, function(err) {
if(err) {
// one of the functions passed back an error so handle it here
return next(err);
}
// no errors so pass control back to express
next();
});
}
Just to make sure I haven't made any mistakes in my test middleware, here is an example of one of them:
'use strict';
var express = require('express');
var router = express.Router();
router.route('/')
.all(function(request, response, next){
console.log('This is middleware BEFORE1');
next();
});
module.exports = router;
Now, when I run my application, I got the following error from npm:
TypeError: Cannot call method 'indexOf' of undefined
at Function.proto.handle (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/express/lib/router/index.js:130:28)
at router (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/express/lib/router/index.js:35:12)
at /Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:610:21
at /Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:249:17
at iterate (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:149:13)
at async.eachSeries (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:165:9)
at _asyncMap (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:248:13)
at Object.mapSeries (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:231:23)
at Object.async.series (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:608:19)
at configurableMiddleWare (/Users/jialunliu/Documents/SOA_project/FAT-LADY/app.js:135:11)
Which is coming from the line
async.series(operations, function(err){})
I am keep getting this kind of error message, saying the function could not read from this array of functions "operations"....
I think you are on the right track, you will just need to tweak a few things. I would register one top level function with app.use() and then do all of your dynamic stuff within that function. Updating my answer to a working example. Be sure to install async first npm install --save async
// define all middleware functions
var middleware = {
mw1: function(req, res, next) {
console.log('mw 1');
next();
},
mw2: function(req, res, next) {
console.log('mw 2');
next();
},
mw3: function(req, res, next) {
console.log('mw 3');
next();
},
mw4: function(req, res, next) {
console.log('mw 4');
next();
}
};
// register our "top level function"
app.use(configurableMiddleware);
var requestCount = 1; // this is just for the working example
function configurableMiddleware(req, res, next) {
var isEvenRequest = requestCount++ % 2 === 0; // simple logic to alternate which "configurable" middleware to use
var operations; // in the real world you could build this array dynamically, for now we just hardcode two scenarios as an example
// Each request to http://localhost:3000 will alternate which middleware is used, so you will see a different log each time
if(isEvenRequest) {
console.log('Even request should log mw2 and mw4');
// .bind(null, req, res) makes sure that the middleware gets the request and response objects when they are invoked,
// as of this point they still haven't been invoked...
operations = [middleware.mw2.bind(null, req, res), middleware.mw4.bind(null, req, res)];
}
else {
console.log('Odd request should log mw1 and mw3');
operations = [middleware.mw1.bind(null, req, res), middleware.mw3.bind(null, req, res)];
}
// invoke each middleware in series - you could also do async.parallel if the order of middleware doesn't matter
// using the async module: https://github.com/caolan/async
async.series(operations, function(err) {
if(err) {
console.log('There was a problem running the middleware!');
return next(err);
}
// all middleware has been run
next();
});
}
For more info on .bind() see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
connect-sequence: a dedicated node module for that specific purpose:
You can just use the module connect-sequence which is designed for that purpose:
npm install --save connect-sequence
see:
the npmjs page: https://www.npmjs.com/package/connect-sequence
or the github project: https://github.com/sirap-group/connect-sequence
and then, here an example of usage:
/**
* Product API
* #module
*/
var ConnectSequence = require('connect-sequence')
var productsController = require('./products.controller')
module.exports = productRouter
function productRouter (app) {
app.route('/api/products/:productId')
.get(function (req, res, next) {
// Create a ConnectSequence instance and setup it with the current `req`,
// `res` objects and the `next` callback
var seq = new ConnectSequence(req, res, next)
// build the desired middlewares sequence thanks to:
// - ConnectSequence#append(mid0, ..., mid1),
// - ConnectSequence#appendList([mid0, ..., mid1])
// - and ConnectSequence#appendIf(condition, mid)
if (req.query.filter) {
seq.append(productsController.filter)
}
if (req.query.format) {
seq.append(
productsController.validateFormat,
productsController.beforeFormat,
productsController.format,
productsController.afterFormat
)
}
// append the productsController.prepareResponse middleware to the sequence
// only if the condition `req.query.format && req.formatedProduct` is true
// at the moment where the middleware would be called.
// So the condition is tested after the previous middleware is called and thus
// if the previous modifies the `req` object, we can test it.
seq.appendIf(isProductFormatted, productsController.prepareResponse)
seq.append(productsController.sendResponse)
// run the sequence
seq.run()
})
app.param('productId', function (req, res, next, id) {
// ... yield the product by ID and bind it to the req object
})
function isProductFormatted (req) {
return Boolean(req.formatedProduct)
}
}
This is open source, PR are welcome!
If you like and use connect-sequence, but if you find bug or need some new features, feel free to post issues or submit pull requests!
Based on the idea behind #Ryan's code I came up with this function. It executes a list of middleware in order binding the variables as needed, allowing everything to be executed by just executeMiddlewareList([middleware1, middleware2...], req, res, next);. For each middlewarereq, res is passed and the callback from async.eachSeries. This means when next() is called inside the middleware, then next one will be handled from the list. If middleware throws an error with next(err), execution will stop and you can manually handle this.
function executeMiddlewareList (middlewareList, req, res, next) {
async.eachSeries(middlewareList, function(middleware,callback) {
middleware.bind(null,req,res,callback)()
}, function(err) {
if (err) return res.status(500).json({error: err});
next();
})
}
function testMid (number) {
return function (req, res, next) {
log.debug('req.test from', req.test, " to ", number);
req.test=number;
next();
}
}
router.get('/test', function(req, res, next) {
m.executeMiddlewareList([test(1), test(2)], req, res, next);
//Output: req.test from undefined to 1
// req.test from 1 to 2
}, function (req,res) {
//Do stuff after the executeMiddlewareList, req.test = 2
})
Finally, I find the answer according the Ryan's, the code would look like this:
function configurableMiddleWare(req, res, next) {
var operations = [];
var middleware;
var sortedConfig = sortConfig(confirguration);
// push each middleware you want to run
sortedConfig.forEach(function(fn) {
switch(fn[0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
console.log(fn[0]);
console.log(middleware);
operations.push(middleware.bind(null, req, res)); // could use fn.bind(null, req, res) to pass in vars
});
console.log('middleware list sorted');
// now actually invoke the middleware in series
async.series(operations, function(err) {
if(err) {
// one of the functions passed back an error so handle it here
return next(err);
}
console.log('middleware get executed');
// no errors so pass control back to express
next();
});
}
app.use('/test', configurableMiddleWare);
The key step is indeed the operations.push(middleware.bind(null, req, res));
Which to be honest, I don't understand what does it mean exactly. I know this is passing "req" and "res" variable into the middleware, but I don't get what the point of the "null" in the front. Much appreciated if someone could help me clarify this.