Can the req object be manipulated within a HTTP request? So is there any chance a request can set a value for req.token? I know that various object properties like req.query, req.body etc. can be freely changed from the outside, but can new properties like req.token be added or is this example code safe?
var auth = function (req, res, next) {
if (isValid()) {
req.token = getToken();
return next();
}
}
app.get('/foo', auth, function(req, res) {
if (req.token) {
// valid request
} else {
// invalid request
}
});
Yes, it's safe, provided you don't overwrite a property that a special meaning (a non-exhaustive list of those can be found here).
It's a commonly used technique, and it's also shown in the Express documentation.
Related
I have a node API which is deployed and run as a Lambda function inside AWS.
// mthd
export const validateHeaderBearerToken = (req, res) => {
if (!req.headers.authorization || req.headers.authorization.split(' ').length !== 2
|| req.headers.authorization.split(' ')[0].trim() !== authConstants.BEARER_HEADER) {
res.status(403)
.send({ message: 'Invalid token' });
throw new AuthenticationException(authConstants.NO_VALID_TOKEN_IN_HEADER_ERROR);
}
return req.headers.authorization.split(' ')[1].trim();
};
and the controller
// controller
searchApiV1Controller.use('/search/', checkAuthentication, searchRouter);
the main service
// mainService
export const checkAuthentication = async (req, res, next) => {
const bearerToken = req.headers.authorization;
logger.info(`[${getCorrelationId(req)}] Authenticating the request`);
const token = validateHeaderBearerToken(bearerToken, res);
let application='liberty';
await checkAuthorize(application);
...
}
When I pass the empty bearer token inside the header, it should respond "Invalid token" message with 403 statuscode. But Since the main service(middleware) is an async method, the call responds with 502 Bad Gateway error due to the AuthenticationException. If we remove this exception it will respond correctly with 403. But the main service method will asynchronously runs through the other sub util methods inside checkAuthentication method. So we can't remove it. What may be the solution for this?
FYI: I don't face any problems while running locally.
When using middleware chains in express, it's typically better form to use the next() function than throwing exceptions/errors. It makes them more composable and also follows the principle of not using exceptions for control flow. Exceptions are for truely exceptional cirumstances, not your standard operations.
If you pass a value to next() it will assume it's an error and skip straight to your error handling middleware. You can also add values onto the res.locals to pass it through middleware chains.
For example:
export const validateHeaderBearerToken = (req, res, next) => {
if (/* checks */) {
return next(AuthenticationException(authConstants.NO_VALID_TOKEN_IN_HEADER_ERROR));
}
res.locals.token = req.headers.authorization.split(' ')[1].trim();
};
Now because you're setting res.locals you don't need to call your validate function a in checkAuthentication, you can use it as middleware, turning that function into something like:
export const checkAuthentication = async (req, res, next) => {
// Assume res.locals.token exists, otherwise we'll have early-exited the entire request.
const bearerToken = res.locals.token
let application='liberty';
let result = await checkAuthorize(application);
// Example logic...
if (result.is_authorised == false) {
return next(/*error*/)
}
}
Now you will never call checkAuthentication if validateHeaderBearerToken fails to find a token. Your router setup will look like:
searchApiV1Controller.use('/search/', validateHeaderBearerToken, checkAuthentication, searchRouter);
Further reading on error handling in express: http://expressjs.com/en/guide/error-handling.html
From inside my expressJS application I have to verify that a cookie token is valid with a back-end server. So the relevant code involved in this is as follows:
app.get('*', (req, res, next) => {
console.log('GET: ' + req.path);
// ...
const payload = JSON.stringify({ authnToken: token });
const opts = { ... authServerOptions };
opts.headers['Content-Length'] = payload.length;
// build request
const restReq = https.request(authServerOptions, result => {
console.log('back-end response' + result.statusCode);
result.on('data', data => {
next(); // token is good now proceed.
});
result.on('error', error => {
res.redirect('somewhere'); // token is bad or timeout
});
});
restReq.write(token);
restReq.end();
}
So the main get function sets the REST request in motion and then just returns without calling next() or anything.
Questions:
Is this the right code for doing this? What happens if the callbacks are never called?
Is the application blocked from processing other requests until the back-end server returns or times out?
If so is there some way of freeing up the thread to process more requests?
Thanks in advance for any help. I haven't found many examples for this code pattern so if there is one a link would be appreciated.
Yes, I think the general idea of your implementation is correct.
I would also suggest, as done in the comments, to use a client such as axios to handle the request in a less verbose and more comprehensive manner, which would leave your code looking something like this:
const axios = require('axios');
app.get('*', (req, res, next) => {
const payload = JSON.stringify({ authnToken: token });
const opts = { ... authServerOptions };
opts.headers['Content-Length'] = payload.length;
axios.post(url, payload, opts)
.then(response => next())
.catch(error => {
console.error(error);
res.redirect('somewhere');
});
});
A bit more to the point, but functionally almost equivalent to your implementation. The one thing you are missing is the onerror callback for your request object, which currently may fail and never return a response as you correctly suspected. You should add:
restReq.on('error', error => {
console.error(error);
res.redirect('somewhere');
});
On the same vein, it would probably be more fitting to call next on result end, instead of doing so while reading response data:
result.on('end', () => {
next();
});
Then you'd be covered to guarantee that a callback would be invoked.
Neither implementation blocks the processing of future requests, as the call to the token validation service is done asynchronously in both cases.
I'm learning express from various tutorials and have an app working locally, but I'd like to better understand what each part of the code does.
I'm a bit stumped with the example in the app.route() section here:
https://expressjs.com/en/guide/routing.html
app.route('/book')
.get(function (req, res) {
res.send('Get a random book')
})
.post(function (req, res) {
res.send('Add a book')
})
.put(function (req, res) {
res.send('Update the book')
})
I can see that app is equal to express(), which is a top level function documented here.
And I can see that the .get(), post() and put() methods are chained to the route() method, which is documented here.
Where I get confused is that the docs state that the arguments for the .get(), post() and put() methods are in this format:
app.get(path, callback [, callback ...])
app.post(path, callback [, callback ...])
app.put(path, callback [, callback ...])
Why do the chained .get(), post() and put() methods not require the path argument, and instead have a singular function as an argument that returns values from the Request (aka req) and Response (aka res) object parameters?
I'm obviously missing something simple, so pointers to documentation that could help me better understand the distinctions between these methods when called straight from app, eg app.get(), and from route(), eg app.route('/book').get() would be much appreciated.
Edit: Basically, I'd like to know if there is documentation that defines the required argument format for the .get(), post() and put() methods when called from the route object returned from calling app.route("/book"), because it does not seem to be what is documented, ie path, callback [, callback ...].
app.route()
As per the docs, the app.route method:
Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware. Use app.route() to avoid duplicate route names (and thus typo errors).
It means, app.route() takes only the path and returns the route object. Which will have all http verb methods to handle middlewares against one path, get, post, delete, post, put, patch etc.
Why?
To simply have routes which have same path but different HTTP requests. Like:
app.route('/books')
.get() // To get the list of objects
.post() // To save a new book.
Individual HTTP methods
On the other hand, express provides individual methods on app for handling HTTP requests. Like app.get(), app.post(), app.delete().
As per docs for post route: HTTP POST requests to the specified path with the specified callback functions.
Why?
For the cases where you don't have one path for multiple HTTP requests. Let's say:
app.delete('/books/:bookId/comments/:commentId', function(){});
The above route is a kind of single route and only used for deleting a specific comment on book.
I hope I was able to clear the difference.
Reference Link for the docs: https://expressjs.com/en/4x/api.html#router.route
Edit:
As no proper Docs are available listing methods provided by route object:
For more information adding github's link to the express router.
https://github.com/expressjs/express/blob/master/lib/router/route.js
Here see the below code of express's router which adds handler on all methods.
methods.forEach(function(method){
Route.prototype[method] = function(){
var handles = flatten(slice.call(arguments));
for (var i = 0; i < handles.length; i++) {
var handle = handles[i];
if (typeof handle !== 'function') {
var type = toString.call(handle);
var msg = 'Route.' + method + '() requires a callback function but got a ' + type
throw new Error(msg);
}
debug('%s %o', method, this.path)
var layer = Layer('/', {}, handle);
layer.method = method;
this.methods[method] = true;
this.stack.push(layer);
}
return this;
};
});
In this file at top, it has:
var methods = require('methods');
methods: https://github.com/jshttp/methods
Thus, the required parameters for the chained methods is unlimited functions as request handlers/middlewares.
The point of chained method is that they have same path.
So you can write this:
app.route('/book')
.get(function (req, res) {
res.send('Get a random book')
})
.post(function (req, res) {
res.send('Add a book')
})
.put(function (req, res) {
res.send('Update the book')
})
instead of
app.get('/book', function (req, res) {
res.send('Get a random book')
});
app.post('/book', function (req, res) {
res.send('Add a book')
});
app.put('/book', function (req, res) {
res.send('Update the book')
});
Which means that if you i.e. change the endpoint it is changed for all methods, you cannot write typo in one method...
I have built , using express() , a variety of methods. for simplicity let's I assume I built 2 POST() functions and I want to be able to use them by themselves and also to concatenate them via middleware for combine usage.
app.post('/create_obj_1' , function (req,res) {
//create Object_type_1
// send Object_type_1 via EXTERNAL API to somewhere
res.json({{ "statusCode": 200, "message": "OK" }
}
app.post('/create_obj_2' , function (req,res) {
//create Object_type_2
// send Object_type_2 via EXTERNAL API to somewhere
res.json({{ "statusCode": 200, "message": "OK" }
}
I want to have a new POST() that can invoke both of the other 2 (but still support stand alone invoking of the original 2
I think it's possible via middleware but I am not sure how - this is how I thought the new POST() should look like -
app.post('/create_obj_all' , function (req,res) {
//I want to invoke the create_obj_1 & create_obj_2 , check all OK, and finish
res.json({{ "statusCode": 200, "message": "OK" }
}
I am not sure how to approach the middleware usage in such case.
On top - how can I connect them to use one each other res? let's say the EXTERNAL API returns some value from obj_1 creation which I want to use in obj_2 post() function..
a Pseudo code of my attempt to use request() inside the middlware_1 -
var middle_1 = function (req, res, next) {
req.middle_1_output = {
statusCode : 404,
message : "fail_1"
}
var options = {
method: 'PUT', url: `EXTERNAL_API`, headers:
{
'cache-control': 'no-cache',
'content-type': 'application/x-www-form-urlencoded',
apikey: `KEY`
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
// CODE THAT DO SOMETHING AND GET INFORMATION
// OLD WAY OF res.send here , to allow using in post.POST() was - res.status(200).send(body);
//res.status(200).send(body);
req.middle_1_output.statusCode = 200;
req.middle_1_output.message = "hello world";
});
next(); // trigger next middleware
}
Given the current example, I don't think you can do it unless you tweak the middlewares for the first two routes a bit:
var middleware1 = function(req, res, next) {
//create Object_type_1
// send Object_type_1 via EXTERNAL API to somewhere
next(); // calling next() triggers the next middleware
};
var middleware2 = function(req, res, next) {
//create Object_type_2
// send Object_type_2 via EXTERNAL API to somewhere
next(); // calling next() triggers the next middleware
};
/**
* This middleware is only used to send success response
*/
var response_success = function(req, res) {
res.json({ "statusCode": 200, "message": "OK" });
}
app.post('/create_obj_1', middleware1, response_success);
app.post('/create_obj_2', middleware2, response_success);
app.post('/create_obj_all', middleware1, middleware2, response_success);
Note that this is a very simplistic solution that I made from your example. The actual implementation will depend on what input each middleware is expecting and what output they generate. Also unlike here, there may also be different middlewares for sending the response.
2nd Part Addressing the second part of your question, if I have got you correctly you want to pass the output from middleware1 to middleware2. You can simply attach the output to the req object before calling next();. Like so:
var middleware1 = function(req, res, next) {
// do something
some_external_api_call(function(error, data) {
if (error) {
// handle the error yourself or call next(error);
} else {
req.middleware1_output = data; // set the output of the external api call into a property of req
next();
}
});
};
var middleware2 = function(req, res, next) {
// check to see if the middleware1_output has been set
// (meaning that the middleware has been called from /create_obj_all )
if (req.middleware1_output) {
// do something with the data
} else {
// handle scenario when /create_obj_2 is called by itself
}
next(); // calling next() triggers the next middleware
};
Notice how you have to account for both scenarios where middleware2 is called from POST /create_obj_all or directly from POST /create_obj_2.
3rd Part You should call next from within the callback. See my above example. This is due to the asynchronous/non-blocking nature of javascript.
function middleware(req, res, next) {
// do something
call_1st_external_api(some_options, function(error, data) {
// executed after call_1st_external_api completes
req.output_of_1st_external_api = data; // store the data of this api call for access from next middleware
next(); // calls the next middleware
// nothing here will be executed as next has already been called
});
// anything here will be executed before call_1st_external_api is completed
next(); // this will call the next middleware before call_1st_external_api completes
}
To handle two external APIs in the same middlewares you have to nest them (or use async or promises):
function middleware(req, res, next) {
// do something
call_1st_external_api(some_options, function(error1, data1) {
// executed after call_1st_external_api completes
req.output_of_1st_external_api = data1; // store the data of this api call for access from next middleware
// executed after call_2nd_external_api completes
call_2nd_external_api(some_options, function(error2, data2) {
req.output_of_2nd_external_api = data2; // store the data of this api call for access from next middleware
next();
});
// anything here will be executed before call_2nd_external_api is completed
});
// anything here will be executed before call_1st_external_api is completed
}
You have to handle all the errors above like I've shown in the 2nd Part which I have not shown in the above example for the sake of simplicity.
Most of my handlers look as follows:
function(req, res) {
var data = ...;
res.render('my_view', data);
}
I know that If I want to return JSON I need to change res.render to res.json, as follows:
function(req, res) {
var data = ...;
res.json(data);
}
when debugging I often want to see the raw data (in JSON format that was computed by the handler). To do that, I (manually) go to the handler callback and change res.render('...', to res.json(.
I am wondering whether there is a way to tell express that if the URL meets a certain condition token (say, ends with .json, or, alternatively, has a ?format=json query param) then res.view will seamlessly delegate to res.json ?
If it is just for debugging purpose then you could make a middleware that would override render method to json.
I will not recommend to use this in production.
In your app.configure add this:
app.use(function(req, res, next) {
if (req.query.json !== undefined) {
res.render = function(name, data) {
res.json(data);
}
}
return next();
});
So what it does: if request has json in query, then it will override render method and will call json instead.
So test it with: http://example.com/test?json