I use node.js and express v4.12. I want to decorate all app.get calls by custom logic.
app.get(/*getPath*/, function (req, res, next) {
// regular logic
});
and my custom logic
customFunc() {
if (getPath === 'somePath' && req.headers.authorization === 'encoded user'){
//costum logic goes here
next();
} else {
res.sendStatus(403);
}
}
The idea is to execute custom logic before code that I already have but I need access to req, res and next objects inside my custom function. And another problem that I need to have app.get arguments to work with requested pattern inside custumFunc.
I tried to implement decorator pattern just like this:
var testfunc = function() {
console.log('decorated!');
};
var decorator = function(f, app_get) {
f();
return app_get.apply(this, arguments);
};
app.get = decorator(testfunc, app.get);
But javascript throws an error.
EDIT
In case app.use() I can only get req.path like /users/22 but when I use it like app.get('/users/:id', acl, cb) I can get req.route.path property and it equals '/users/:id'and this is what I need for my ACL decorator. But I don't want to call acl function for each endpoint and try to move it to app.use() but whith req.route.path property.
Example of implementing your middleware:
app.use(function(req, res, next) {
if (req.path==='somePath' && req.headers.authorization ==='encoded user'){
//costum logic goes here
next();
} else {
res.sendStatus(403);
}
});
If you want to pass middleware only in one route you can implement like this:
app.get(/*getPath*/, customFunc, function (req, res, next) {
// regular logic
});
You are trying to build a middleware. Simply add your decorator to the application via app.use.
Related
I'm creating a helper method in my code which I can use for authorizing users,
I am creating a function called "usePermissions" following the react way but on the back-end.
The usePermissions() will return true or false based on some x, y and z.
But this method depends on the request object,
so in order to make it work, I'll everywhere have to call
usePermissions(req),
ex:
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = usePermissions(req)
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
Is there a way to package this "usePermissions" helper method with the request object?
I want it to automatically have the request object, I don't want to keep passing it as a variable,
how to make it have the request object without having to pass it as an argument to it, is it possible?
ex:
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = usePermissions() // <-- see, I want to not pass the req object
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
You can create a middleware that will call usePermissions and append result to the Request object, so it will become available in all your handlers without explicitly calling it.
Your middleware code might look something like (read more about using middlewares in Express app)
export function getUsePermissions = (req, res, next) => {
const { verify } = usePermissions(req);
req['verify'] = verify
// moving to the next middleware in chain, now Request object has a verify property
return next()
}
and in your express app, add getUsePermissions middleware
express.use(getUsePermissions);
now you can use extract usePermissions from request object in your handlers:
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = req['verify'];
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
Yes, you can use it as middleware and you will get a response, request and next object by default.
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', usePermissions , async (req, res, next)=>{
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
usePermissions Function will be like this:
const usePermissions = (request, response, next) => {
const isPermitted = 'you logic';
if (isPermitted) {
next(); // this will automatically call the next middleware
// in your case it will call the next callback in the userRouter
} else {
response.send('You don't have permission'); // just added a sample text for brevity
}
}
You can read more about middlewares here.
I am trying to replicate a middleware you might have in express on an azure function.
For example:
router.get('/protectedEndpoint', secured(), function (req, res) {
where the secured() function is a middleware that will send next() if valid.
the problem with azure is it is done in the style
module.exports = function (context) {
and i am unsure of how to run a middleware with next() in this
here is a dumb example of what that function may look like:
module.exports = function () {
return function secured (req, res, next) {
if (req.user) { return next(); }
req.session.returnTo = req.originalUrl;
res.redirect('/login');
};
};
With azure function you can use azure-middleware engine to add middleware just like you do with Express. By this method you can do the same thing as you were doing with Express.
The link for this Engine is as follow azure-middlware
var MiddlewareHandler = require('azure-middleware')
module.exports = new MiddlewareHandler()
.use((ctx) => {
secured();//Your middleware function
ctx.next();
})
.use(async (ctx) => {
//Your controller or your main function script
ctx.log.info('Im called third');
ctx.done(null, { status: 200 });
})
.catch((error, ctx, msg) => {
ctx.log.info(error); // ERROR!
ctx.next();
})
.listen();
Yes well that example on the page is cryptic at best, and as it turns out, the package does not have full Typescript support yet either.
If anyone reading this is looking for a Typescript solution, you will have to embrace nested try catch statements to first verify the token (if the use case is Authentication), then proceed with the service to call any protected resources.
I'm struggling a lot trying to understand the logic behind the ExpressJS module, in particular i'm focusing on the implementation of the middlewares chain.
My objective here is to understand how is it possible to implement the logic of a server listening for requests, and once arrived, passes the request packet through an array of functions each of which:
should be able to modify the packet for the next middleware
should be able to send the response back but it should not prevent other middlewares to respond as well, so there should be only one response at the end of the chain (i guess)
should access some function to call in order to run the next middleware in chain, but without the need to pass it the request and response parameters (e.g. just calling "next( )" )
Note on point 1
I imagine that with languages like javascript is possible to pass the object around using concepts like closure, but i would like to understand it in a language-independent way and use javascript features only if it provides clever ways to handle it simply, otherwise i'll write the logic as i was using any other language.
So my big questions are:
How should the request handling be implemented?
How do i allow every middleware to modify the packet?
How do i pass the updated packet to the next middleware?
How do i implement the "next" function to pass to every middleware?
Thank you
Broadly, this is how a chain of middlewares with a next function can be called.
function myMiddleware(req, res, next) {
// do something with req or res
next();
}
const mws = [
myMiddleware,
anotherMiddleware
];
/**
* Calls a chain of middlewares.
*
* mws is an array of middlewares
*/
function callMwChain(req, res, mws) {
if (mws.length === 0) {
// We're done here
return;
}
/**
* Take the first middleware
*/
const firstMw = mws[0];
/**
* Call it, and give it a next function that continues the chain
*/
firstMw(req, res, () => {
callMwChain(req, res, mws.slice(1));
});
}
I've written this as a recursive function, but it can also be rewritten as stack. This is easier though.
Express middlewares have a lot of bonus magic, so their implementation is going to be more complex, but this is broadly how it works.
Note that there's no event-loop in sight.
Here's a little working server that supports middleware and GET routes. It uses a similar calling convention on the route handlers as Express (req, res, next), but has far, far, far fewer features than Express. But, hopefully you can see how the dispatch function cycles through the routes, advancing to the next route only if the previous handler calls next().
You can actually run this code and define routes and play with it (though it is definitely barebones basic):
const http = require('http');
function dispatch(req, res, array, index, cb) {
console.log(req.method, req.url, array, index);
if (array && array.length > index) {
let item = array[index];
if (!item.path || item.path.toLowerCase() === req.url.toLowerCase()) {
item.fn(req, res, function() {
// previous route called next(), so we advance to the next item in the array
++index;
dispatch(req, res, array, index, cb);
});
return;
}
}
cb();
}
const server = http.createServer((req, res) => {
// incoming request here, initiate calling the middleware
dispatch(req, res, server.middleware, 0, function() {
if (req.method === "GET") {
dispatch(req, res, server.gets, 0, function() {
// done with gets here, apparently didn't send a response
res.statusCode = 404;
res.end();
})
} else {
// ... fill in code for other methods here
res.statusCode = 404;
res.end();
}
});
});
// server route implementation
// save arrays for each type of route handler
// upon a request, run all the middleware requests, then advance
// to the routes for the right type of request and see if any of them
// match the URL. If so, run them.
server.middleware = [];
server.gets = [];
server.posts = [];
server.use = function(fn) {
server.middleware.push({path: "", fn: fn});
}
server.get = function(path, fn) {
server.gets.push({path, fn});
}
// route definitions
server.use((req, res, next) => {
console.log(req.url);
req.myCustom = "hi";
next();
});
server.use((req, res, next) => {
console.log(req.myCustom);
next();
});
server.get("/", (req, res, next) => {
res.write("Hello World");
res.end();
});
server.listen(80);
I have a general question on how you handle services and routes in node.js. Would you handle the response directly in the service or would you leave that to the route? Here's what i mean in code
Like this
Route
router.get('/', (req, res, next) ==> {
someService.someMethod(req, res);
});
Service
const someMethod = (req, res) => {
try {
var something = await someOtherMethod(req.body.someParameter);
return res.status(200).send(something.data);
} catch (err) {
return res.status(500).send({msg: err.message});
}
}
Or this
Router
router.get('/', (req, res, next) ==> {
try {
var something = await someService.someMethod(req.body.someParameter);
res.status(200).send(something.data);
} catch (err) {
res.status(500).send({msg: err.message})
}
});
Service
const SomeMethod = (Input) => {
return someOtherMethod(Input);
}
The first way would make the routers much simpler and cleaner especially if the use the service in multiple routes, but on the downside I always need to supply the res and req and I will run into problems if I want to use the service internally. I'm tending to the second method.
How do you design your services?
I would go for router.get('/', RootController)
const RootController = (req, res) => {
// extract what you need from the request
const param = req.body.param;
// calculate what you need in a pure function `businessLogic`
const result = businessLogic(param);
// send the response
return res.send(result);
}
This way you get a separation of concerns - your root controller is responsible only for handling / requests - getting a response for a request. All "business logic" is done in a pure function (you can easily test it without any HTTP request contexts/mocks, it can be reused somewhere else, for example in different controller).
I use the following architecture:
1. Route
2. Controller
3. Services
Your route is the one validating the input, your controller is the one handling all the logics and calling the services and returning the final result to your route.
I am using Express.js as http server. Defined all my routes.
Most endpoints need to verify session before returning a response. E.g. below code serves users in the system and list of services respectively:
function getUsers(req, res, next) {
verifyUser(req, res, next, function () {
//serve users
});
}
function getServices(req, res, next) {
verifyUser(req, res, next, function () {
//serve services
});
}
You probably noticed there is a verifyUser function which validates the session. Which is as below.
function verifyUser(req, res, next, callback) {
var sessionKey = req.cookies.sessionKey;
var user = users.userBySession(sessionKey);
if (user) {
callback(req, res, next, user);
} else {
res.status(401).send({
message: 'Unauthorized'
});
}
}
As you can see I keep passing in req, res and next parameters along with a callback whenever I use this function.
I tried to use apply function to make it easier. Changed my getUsers function like this:
function getUsers(req, res, next) {
verifyUser
.apply(null, arguments, function () {
//serve users
});
}
The problem with this approach is callback is not passed into verifyUser function. And I don't really like passing null as scope with each call.
How can I achieve this by writing less and better code ? Any ideas?
You could use bind to create a 'partial function':
// create bound responseHelper object
var responseHelper = verifyUser.bind(null, req, res, next);
// usage
responseHelper(getUsersCallback); // same as verifyUser(req, res, next, getusersCallBack);
I think you're looking to turn verifyUser into a middleware function.
function verifyUser (req, res, next) {
var user = // yadda yadda session stuff
if (user) {
req.user = user; // [1] what you do to the req object here...
} else {
return res.status(401).send({ message: "No way Smokey Joe"});
/**
* alternatively, do something like
* var err = new Error("Not authorized");
* err.statusCode = 401;
* return next(err);
*
* this will kick off Express' error handling mechanism,
* which you should read about in the docs (see the link below)
*/
}
next();
// very important to call next after this verifyUser has done its job
// if you don't, the next middleware won't go off,
// and the request will just hang
}
function getUsers (req, res, next) {
// [2] will show up on the req object here, assuming you chain these
// two functions together as middleware
}
app.get("/users", verifyUser, getUsers);
app.get("/services", verifyUser, getServices);
// here's a route that needs no session auth, so no need to verifyUser
app.get("/latest-posts", getLatestPosts);
When you tell Express to use a function or attach a function to a route path via get('/my/route', hanlderFun) or some such, you've basically turned handlerFun into a middleware.
You can define however many middleware as handlers on a route as you like, and they'll all execute in turn as long as you keep calling next.
app.post("/checkout", verifyUser, tallyCart, checkInventory, doPayment, sendInvoice);
The job of next is to pass control from the current middelware to the next one. It's an object
You can do other stuff with next, too, which you should read up on in the docs.
http://expressjs.com/en/guide/writing-middleware.html
http://expressjs.com/en/guide/using-middleware.html
The docs on routing have good info on middleware as well:
http://expressjs.com/en/guide/routing.html
For extra credit, check out error handling middleware, too:
http://expressjs.com/en/guide/error-handling.html