router.use((req, res, next) => {
if(req.originalUrl == '/api/test'){
//does stuff
res.send(result);
}
next();
})
vs
route.get('/api/test', (req, res) => {
//does stuff
res.send(result)
}
I'm rather unfamiliar with the whole HTTP web application safety conduct so I need to ask, is there any vulnerability or downside if I use the first approach to resolve some of the route destination?
I'm rather unfamiliar with the whole HTTP web application safety conduct so I need to ask, is there any vulnerability or downside if I use the first approach to resolve some of the route destination?
There are two main differences between router.use() and router.get() and one is somewhat relevant here:
router.use() matches ANY http verb such as GET, POST, PUT, OPTION, PATCH, etc... whereas router.get() only matches GET requests, router.post() only matches POST requests, etc..
router.use() uses a "loose" match algorithm where the requested route only has to start with the path designation on the route, not match it entirely.
For the first point, your middleware handler is doing res.send(response) for all http verbs that have a request path of /api/test. That is probably not what you want and is not how you should write the code. You should have your code respond only to the http verbs for that path that you actually intend to support and do something useful with. Other verbs should probably respond with a 4xx status code (which would be the default in Express if you don't have a handler for them).
For the second point, your middleware handler is generic (no path set) and you are already checking for the entire path so that point is not relevant.
With one small addition, I'd say that your middleware approach is fine. I'd add a check for the req.method:
router.use((req, res, next) => {
if(req.method === "GET" && req.originalUrl == '/api/test'){
//does stuff
res.send(result);
// return so the rest of the route handler is not executed
// and we don't call next()
return;
}
next();
});
All that said, you can also probably solve your problem in a bit more generic way. For example, if you put a specific route definition in front of this middleware, then it will automatically be exempted from the middleware as it will get processed before the middleware gets to run. Routes in Express are matched and run in the order they are declared.
router.get('/api/test', (req, res) => {
//does stuff
res.send(result)
});
router.use((req, res, next) => {
// do some middleware prep work on all requests that get here
// ...
next();
});
// other route definitions here that can use the results of the prior middleware
For example, this is very common if you have some routes that need authentication and others that do not. You put the non-authenticated routes first, then place the authentication middleware, then define the routes that want to be behind the authentication check.
There is no "exact" vulnerability, but many, many drawbacks. The major difference here is that the first piece of code is what we call "a global handler". It is executed on each and every request. The second piece is just a specific route.
When you make a request, express starts to evaluate the pipeline of things it needs to do in order to return a response. First it executes all global handlers (like the first example). Then it starts matching the route against a list of handlers, and if it finds it - it executes the function.
What you risk using the first approach is breaking the chain and not executing all global/local handlers properly. Some of those might do specific things (like preventing you from some type of attacks). Then, using the second approach, you define way more things than just a global handler: you define the endpoint, as well as the method (in your case it's a GET request).
When the matcher finds your route, it does break the chain automatically for you (in the simplest scenario).
Also note that in the first example, you'd have a single point with tons of if-else statements to figure out what you want to do with this request. It basically mitigates the need of express whatsoever...
Express is made in a way that it supports multiple "middlewares". If you want to do specific pieces based on some action, here's what you can do:
router.get('/users',
handler1(req, res, next) {
if (req.something) {
next(); // we skip the logic here, but we go to handler2
} else {
// do your magic
res.send(response);
}
},
handler2(req, res, next) {
// you skipped the action from handler1
res.send(anotherResponse); // you MUST send a response in one handler
}
Related
In express I have a handler for a route ex:
router.get(`${api}/path/:params/entrypoint`, routeHandler);
In this example 'routeHandler' function has a lot of logic doing various things. I'd like to break 'routeHandler' into smaller methods to ease readability and testability. So instead of:
routeHandler(req, res) {
//many lines of code
}
We could have:
routeHandler(req, res) {
helperOne(req, res);
helperTwo(req, res);
}
helperOne(req, res) {
//do stuff
}
helper2(req, res) {
//do stuff
}
I am being told not to do this by a coworker who is pretty senior, but I do not understand why. Does anyone know of any issues that can arise by passing the response or request objects into helpers? I can not think of any and google isn't revealing any clear answer.
Thanks!
Does anyone know of any issues that can arise by passing the response or request objects into helpers?
Yes you may run into some problems when passing those parameters, especially res. For example you may res.send multiple times (one in each function) which will raise an exception.
Scenario
A more concrete example is this
routeHandler((req, res) => {
helperOne(req, res);
helperTwo(req, res);
});
Based on some conditions, I want to stop and return an error from helperOne and not go execute any code from helperTwo. My definitions of these functions are like this
helperOne = (req, res) => {
const dataPoint = req.body.dataPoint; // a number for example
if (number > 10) {
return res.send("This is not valid. Stopping here...");
} else {
console.log("All good! Continue..");
}
}
helperTwo = (req, res) => {
res.send("Response from helperTwo");
}
Then let's say I do have req.body.dataPoint = 10, and I'm now expecting my routeHandler to stop after the return res.send in the first block of my if statement in helperOne.
This will not work as expected though, because the return will concern only helperOne which is the returning function. In other terms it won't propagate to routeHandler.
In the end an exception will be raised because routeHandler will call helperTwo and try to send a response again.
Solution
Don't send req or res. Just pass the data you need and handle the reponse in your main handler
An even better alternative is to use Express middlewares. Since you have multiple "sequential" handlers, you can chain multiple middlewares, which is closer to the standard Express.JS way
One reason to avoid doing this is that you're tightly coupling your helper functions to routeHandler, and encouraging complexity in the helpers. If you break up your helper functions so they only have a single responsibility, it's likely you'll only need to pass in a subset of the request.
Why are you passing in res, Are you sending a response from inside the helpers? Without knowing the details of your routeHandler implementation, I would see if you could handle logic in the helpers, but have them each return a value and keep the response-sending in the main routeHandler function. Here's a simple example:
handleRoute('/users/:userID', (req, res) => {
const { userID } = req.params;
const idIsValid = validateUserID(userID);
if (!idIsValid) {
return res.status(400).send('Invalid user ID!');
}
...
});
I'm using a router middleware to check if the request is valid or not. Also, I'm using a global Error handler middleware at the end of the server.js to catch and process all the errors.
Inside the router, I'm facing a problem implementing this. The following code will state the problem clearly.
Error middleware errorHandler.js
module.exports = function(err, req, res, next) {
//some logic
return res.status(400 or 500).json({err});
};
My server.js (short version)
const express = require('express');
const app = express();
app.use('/account/:accid/users', userRouteHandler);
const errorHandler = require('PATH to errorHandler.js file');
app.use(errorHandler);
app.listen(3000, () => console.log(`Example app listening on port 3000!`));
userRouteHandler file
var express = require('express');
var router = express.Router({ mergeParams: true });
router.use(express.json());
// middleware to use for all requests
router.use(function(req, res, next) {
/**** PROBLEM / CONFUSION ****/
checkIfAccountIdExists(req.params.accid) //if exists resolve(count) else reject(new Error)
.then(next) //I want to allow calling the get if it exists
.catch(next); //for error it should call the global error handler
});
router
.get('/', function(req,res,next){
console.log("entering here");
findAllTheUsers({accid:req.params.accid})
.then(users=>res.status(200).json(users);
.catch(err=>next(err)); //ensure it can call the global error handler
});
The code always calling the get route. As for both, I'm calling next(). If I call next() only inside then(), my global error handler will get skipped if any error occurs.
One way could be directly calling my errorHanler function within the catch block. but I want to keep my code separate and don't really want to require my errorHandler file within each route.
How can I achieve this?
Without knowing the behavior of your checkIfAccountIdExists function, my supposition is that it returns a promise that always resolves with false|undefined|null; something like this:
checkIfAccountIdExists(id).then((exists) => console.log(exists));
// outputs "false" or "undefined" or "null"
It's my supposition, because otherwise your .get('/') route shouldn't even enter, given how next() works.
Understanding next():
Calling next() (to the shame of Express) has always been confusing without having in-depth knowledge of it. It basically works in 3 ways:
next() (no arguments) -> pass execution to next callbacks in the
route
next('route') (the string 'route' argument) -> bypass any remaining callbacks in the route (moves into any routes that follow)
next(err) (any other truthy parameters, aside from 'route') -> invokes the error handlers.
In your specific case, my assumption is that checkIfAccountIdExists() solves with false|undefined|null, essentially invoking next(err) signature, but because err is not truthy, it's treated as a next() signature, moving onto the next route callback. I'd check the sanity of checkIfAccountIdExists() for this.
Using next() with Promises
When using Promises, it's important to remember that your first argument of .then() (meaning your fulfillment handler) will always receive a parameter!
promise.then(callback);
function callback() {
console.log(arguments.length); // ALWAYS 1
}
For this reason, you should always avoid setting next() as a fulfillment handler to promises. Otherwise, once your checkIfAccountIdExists() will solve with true, it will actually invoke the next(err) signature!
Always write: promise.then(() => next()) instead of promise.then(next), to make sure you call next without arguments.
Writing promise.catch(next) is however fine, because it's the same as promise.catch((err) => next(err))
A bit more info on Promises
Also, Promises (thenables) allow for two arguments on .then(), one being a fulfillment handler and one a rejection handler.
Example: promise.then(onFulfillment, onRejection) which is similar to calling promise.then(onFulfillment).catch(onRejection) except of how errors are caught!
For .then(onFulfillment, onRejection) any errors that are thrown
inside onFulfillment are never caught by onRejection.
For .then(onFulfillment).catch(onRejection) any errors that are thrown
inside onFulfillment are also caught by onRejection
In your case, that means you can safely write
checkIfAccountIdExists.then(() => next(), next);
because the next route (onFulfillment arg) will also handle errors.
Note: errors inside synchronous code will be caught by express.
More info:
Express Error Handling
Promise.prototype.then()
I'm using node with express 4.0. I can't find anything on the internet (including the docs) about embedding asynchronous code in a route.
With a middleware it's quite simple:
app.use('/something', function (req, res, next)
{
doSomethingAsync(function(err, probablySomethingElse)
{
// probably some error checking
next();
});
});
The problem with routes is that there is no next callback, so how does express know when to move to the next job?
app.get('/something', function (req, res)
{
res.render('someTemplate');
// no next() here, but it still works
});
If I had to guess, I'd say that express moves to the next task right after the above function exits. But out of curiosity I've launched the following code...
app.get('/something', function (req, res, next)
{
console.log(next);
});
...and there actually is some next callback passed. So what's going on here? How does it work behind the scenes? And how can I put asynchronous code there?
Express will wait until you call res.render to close the socket. Which means that you can pass res.render into a callback that takes X secs to execute and everything will still work.
The next allows you to go to the next route that maps your value, you can find a very good explanation here: What is the parameter "next" used for in Express?
But under what you are asking here. The moment render() is called on the res object, then data will be sent and the socket closed.
I'm new to Node and Express and the other layers that go along with building web apps with node and the request and response parameters are really confusing me. My confusion lies in the fact that those two parameters are often present in a function but oftentimes one or both of them isn't declared. Also, much of the time an additional parameter will be thrown in, like 'next' or something else. For example, I have the following router for an API:
router.route('/teams')
// create a team at POST http://localhost:8080/api/teams
.post(function(req, res) {
var team = new Team();
team.name = req.body.name;
team.location = req.body.location;
// save the new team and check for errors
team.save(function(err) {
if (err) {
res.send(err);
};
res.json({ message: 'Team created!' });
});
})
// GET all the teams at GET http://localhost:8080/api/teams
.get(function(req, res) {
Team.find(function(err, teams){
if (err) {
res.send(err);
};
res.json(teams);
});
});
Both .post and .get call a function with req and res as parameters, but req is never used. So how does the function know what to do with req or res if they're not defined and used or not used in completely different orders? Or if I named them something completely different?
What exactly is happening with requests and responses? Sorry for my ignorance. I've read the documentation but it's not clicking.
Thanks.
When you use expressApp.use('/some/route', myRouteHandler); Express will listen for requests for that route, and when it's hit, it will call the function you provided (callback). It will give it three parameters: request and response, and next. (Actually could be four, but lets keep things simple.)
So, your callback might be defined like this:
function myRouteHandler(a, b, c) {
// do stuff
};
or like this:
function myRouteHandler(req, res, next) {
// stuff
}
or simply:
function myRouteHandler() {
// stuff
}
Whatever you do, doesn't matter. When the app is started, express listens for requests.
When one of them matches the route (/some/route), express will, in its internal workings, call the function you provided, like this:
myRouteHandler(requestObject, responseObject, nextMiddleware);
So in the first case, you can access the request (like, request headers, full url, caller IP address or similar) by using req. In your second case, you'll access it by calling a. In the third case, you can use arguments[0].
By convention, people will use the form: myCallback(req, res) and know that Express will put the request object as the first param, and response as the second. The response object actually has a method end(), so you can end the request. If there is also a next() object, you can call the next middleware.
Say you have a route defined like this:
app.use('/api/users', checkAuthorizationHandler);
app.use('/api/users', makeSureTheIPisFromOurInternalNetwork);
app.use('/api/users', nowHandleTheResponse);
Each of those handlers gets a third param. If you name it, you'd usually in your function declaration call it 'next' parameter. It means, the next function in order.
Say your function checkAuthorizationHandler(req, res, next) will check for req.headers('auth') token and if it's ok, it will in the function body, call next().
Then the function makeSureTheIPisFromOurInternalNetwork(a, b, c) is called. It will check that the a.ip is a LAN ip address and call c();
Finally your function nowHandleTheResponse() will find all users, and respond with a JSON object of the users: arguments[1].json([user1, user2, user3]);
So, first param is something that express gives you, it's the request, second is response, third is a next middleware function in line. No matter what you call them, they are there.
P.S. You can also declare your middleware with four params:
function(error, req, res, next);
Express will actually check your function and if it finds that you have four params and not two or three, it will give you any errors thrown by the middleware that ran earlier in the chain. Meaning, if your checkAuthHandler says next(new Error('Not authorized'));, your next function might check for that error, and if it's present, not give results. Often however will the middleware which detects errors just res.end('some error message');
If I haven't confused you enough, just say, I've got more where this came from :)
It is the framework convention. The first argument is the request, the second is the response. If you're declaring a middleware (.use), the third argument is the next middleware in the chain.
You can name these variables however you want, as long as you know the order. You could have something like: .post(function(a,b) {}); and then the request is represented by variable a, and response by variable b.
If, for whatever reason, you don't need the request, only the response, you still have to have a first argument, as the response is represented by the second argument.
In javascript, there's no method overload like in Java, for example (maybe here's where you getting the confusion from). A function is represented by its name, not how many arguments it takes. Here's a simple example:
function logToConsole(level, message) {
if (!message) {
message = level;
level = 'INFO';
}
console.log('['+level+']', message);
}
logToConsole('My message'); // prints out: "[INFO] My message"
logToConsole('WARN', 'My message'); // prints out: "[WARN] My message"
Did you notice how we defined a default value for level, based on the existence of message?
Hope this clarifies things a bit.
Request, response and next are passed to all middleware functions. The request object contains information about the HTTP request, and the response object is used to handle the request. The Expressjs documentation details these objects. The next() call is used in something called the dispatcher, a middleware function may or may not call next() depending on usage. Next simply calls the following middleware.
Here is an example of using next():
function helloWorld(req,res,next){
console.log('Hello, world!');
next();
}
// This function doesn't call next(), therefore it will
// not call the subsequent middleware in the dispatcher
function goodbyeWorld(req,res,next){
console.log('Goodbye, world!');
}
app.use(helloWorld);
app.use(goodbyeWorld);
Output:
Hello, world!
Goodbye, world!
Now let's reorder the middleware
app.use(goodbyeWorld);
app.use(helloWorld);
Output:
Goodbye, world!
The helloWorld function is not called. Notice the importance of middleware order and the next() function call.
Hopefully this is a simple one, but I have some custom middleware which I want to return a 404 or 401 etc to the user and stop the propagation of other handlers etc.
I was expecting I could do something like:
function SomeMiddleware(req, res, next) {
if(user.notRealOrSomething)
{ throw new HttpException(401, "Tough luck buddy"); }
return next();
}
However cannot find any specific info about how is best to do this.
You are suppose to pass errors to the next() function.
function SomeMiddleware(req, res, next) {
if(user.notRealOrSomething) {
return next(throw new HttpException(401, "Tough luck buddy"));
}
next();
}
Any argument you pass to next will be considered an error except 'route' which will skip to the next route.
When next is called with an error the error middleware will be execute.
function (err, req, res, next) {
// err === your HttpException
}
Express.js will treat any middleware with 4 arguments as error middleware.
Error-handling middleware are defined just like regular middleware, however must be defined with an arity of 4, that is the signature (err, req, res, next):
All of this is pretty well documented at: http://expressjs.com/guide/error-handling.html
Nothing prevents you from using the response object:
res.send(401, "Tough luck buddy");
next('invalid user');
The code as written in the answer should work just fine, since the code is synchronous - that is, it's not async and it doesn't hand next() off to a callback anywhere. Per the Express.js docs (emphasis mine):
Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it.
So in synchronous middlewares and handlers, just throw the error, Express will catch it and pass it to the error middleware.
For asynchronous middlewares, you need to pass the error object to the next() function without throwing it. The example from the docs applies just as well to middlewares as it does to route handlers (which are in fact also middleware), to adapt it to your example:
function SomeMiddleware(req, res, next) {
user.checkRealOrSomethingAsync(isReal => {
if(!isReal) {
// Note: Pass the exception, do not throw it!
next(new HttpException(401, "Tough luck buddy"))
}
next()
})
}
Note that, as per the Express docs, this means you must catch any errors thrown in async middlewares or routes and pass them into next().
However! Starting in Express 5, you don't need to catch errors thrown within async methods, the error will be propagated as normal without you doing anything. express-async-errors package will patch Express.js 4 to work similarly. Note this only applies to errors at the "top level" of the async function - if you're mixing async and callbacks, you'll still have to catch errors inside the callbacks.