I have some code from a tutorial and i am trying to understand it. I can't figure it out what's the purpose of this middleware function:
app.use((req, res, next) => {
res.locals.path = req.path;
next();
});
res.locals doc says:
An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during that request / response cycle (if any).
This middleware sets the path part of the request URL to res.locals object and call next middleware.
You can use the res.locals.path to access the value in your controller later
Related
I am trying to figure out how middleware works in Express.
Whilst I understand the concept of middleware, I am confused by the middleware parameters.
Here is an example from the offical docs regarding middleware:
app.use('/user/:id', function (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
In this example, I can see there are two functions that act as two middleware which are executed one after the other before this specific route is handled.
But what are the parameters passed to these functions?
Are req and res just "empty" objects?
If so how are we able to reference the property req.originalUrl?
And if not, where is that object and its properties coming from?
They also use res.send in the tutorial, so therefore the res object seems to also have properties and not be an "empty" object.
(I understand that next is a call back argument).
Summary
The request object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on.
The response object represents the HTTP response that an Express app sends when it gets an HTTP request.
Middleware functions are functions that have access to the request object, the response object, and the next function in the application’s request-response cycle. The next function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.
Routes can have chained methods attached (for GET, POST and DELETE requests) that take middleware functions as arguments.
The request object is the data initially received from the request, which can be modified as it passes through various middleware functions, and the response object is the data sent out.
Example Middleware
Below is an example middleware function you can copy and paste at the beginning of your app:
/**
* An example of a middleware function that logs various values of the Express() request object.
*
* #constant
* #function
* #param {object} req - The req object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on. In this documentation and by convention, the object is always referred to as req (and the HTTP response is res) but its actual name is determined by the parameters to the callback function in which you’re working.
* #param {object} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request. In this documentation and by convention, the object is always referred to as res (and the HTTP request is req) but its actual name is determined by the parameters to the callback function in which you’re working.
* #param {Function} next - `next` is used as an argument in the middleware function, and subsequently invoked in the function with `next()`, to indicate the application should "move on" to the next piece of middleware defined in a route's chained method.
* #see {#link https://expressjs.com/en/4x/api.html#req|Express Request}
* #see {#link https://expressjs.com/en/4x/api.html#res|Express Response}
* #see {#link http://expressjs.com/en/guide/writing-middleware.html|Writing Middleware}
*/
const my_logger = (req, res, next) => {
console.log("req.headers: ");
console.log(req.headers);
console.log("req.originalUrl: " + req.originalUrl);
console.log("req.path: " + req.path);
console.log("req.hostname: " + req.hostname);
console.log("req.query: " + JSON.stringify(req.query));
console.log("req.route: " + JSON.stringify(req.route));
console.log("req.secure: " + JSON.stringify(req.secure));
console.log("req.ip: " + req.ip);
console.log("req.method: " + req.method);
console.log("req.params:");
console.log(req.params);
console.log("==========================");
//if next() is not invoked below, app.use(myLogger) is the only middleware that will run and the app will hang
next();
}
// called for all requests
app.use(my_logger);
Example Routes
Below are some example routes.
The routes have chained methods attached that take middleware functions as arguments.
// some example routes
app.route("/api/:api_version/pages")
.get(api_pages_get);
app.route("/api/:api_version/topics")
.get(api_topics_get)
.post(api_login_required, api_topics_post)
.delete(api_login_required, api_topics_delete);
app.route("/api/:api_version/topics/ratings")
.post(api_login_required, api_topics_ratings_post);
Using next() in a middleware function
In the above example, you can see some methods have two middleware functions as arguments.
The first one, api_login_required, verifies login credentials and, if successful, calls next() which prompts the next middleware function to run.
It looks like this:
const api_login_required = (req, res, next) => {
// req.user exists if the user's request was previously verified, it is produced elsewhere in the code
if (req.user) {
next();
} else {
return res.status(401).json({ message: 'Unauthorized user!' });
}
}
Middleware without next()
However, the get() method attached to the route handler for /api/:api_version/pages only has a single middleware function argument: api_pages_get.
As shown below, api_pages_getdoes not call next() because there are no middleware functions that are required to run after it.
It uses the send() and json() methods of the response object to return a response.
const api_pages_get = async (req, res) => {
var page_title = req.query.page_title;
var collection = mongo_client.db("pages").collection("pages");
var query = { page_title: page_title };
var options = { projection: { page_title: 1, page_html: 1 } };
try {
var page = await collection.findOne(query);
// if there is no result
if (page === null) {
res.status(404).send('404: that page does not exist');
return;
}
// if there is a result
else if (page !== null) {
res.json(page);
return;
}
} catch (err) {
console.log("api_pages_get() error: " + err);
res.send(err);
return;
}
}
Notes on middleware
Some other notes I've previously written for my own reference that may help:
Middleware, or middleware functions, have access to the Express request and response objects and are passed as arguments to a route's chained method (or on all requests if passed as an argument to an instance of the use() method defined early in your code).
next is used as an argument in the middleware function, and subsequently invoked in the function with next(), to indicate the application should "move on" to the next piece of middleware defined in a route's chained method.
If a middleware function does not invoke next(), it will not move on to the next piece of middleware defined in a route or method handler.
Additionally, if next() is not used, and a terminating action, ie a response, is not defined in the function, the app will stay in a "hanging" state.
Do req and res are just "empty" objects?
No, req and res are never empty and are in fact same which are passed on to each middleware. You can also modify the req and res objects and the modification will persist in all next middlewares.
You can see all the available fields on req and res here respectively - request object and response object.
You can always access req and res at any point in a middleware. If you wish to the end the request response cycle, you can just use the response object and send a response like res.send(200). This will end the req-res cycle and you need not call the next().
But what paramters are given to this functions ?
You don't need to pass any parameter to this function. Express will alwasy pass the req, res and next to any middleware defined. It's the format you can assume that express uses and and all middlewares should follow.
Note that if you don't end the req-res cycle, you must call the next() which will pass on the control to the next middleware. If the middleware does not end the req-res cycle and also does not call next(), the request will keep hanging and may perhaps just timeout on the client side.
If I understand you correctly, the confusing part are the objects passed to the middleware functions? In the docs you've linked there is already an explanation for those (See below).
"But what paramters are given to this functions ?."
Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.
(Source)
"Do req and res are just "empty" objects ?, if so how come we are using the field req.orginaleUrl ? and if not where is that object and
its field came from ?"
If you follow the links, you'll discover following explanation for the request object:
The req object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on.
(Source)
The originalUrl property mentioned in your question is a property of the req object.
and the response object:
The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
The send is a method assigned to the res object, which will send a HTTP response.
(Source)
I am submitting a simple contact form in my website's footer (in footer.pug):
form(method="POST" action="contact_form")
input(type='email' name='ct_email' data-name='ct_email' required)
textarea(type='text' name='ct_message' data-name='ct_message' required)
button(type='submit') Send
Since the form is in a template, and the footer template is used throughout the site, the form can be submitted from various routes:
/contact_form
/route1/contact_form
/route1/de/contact_form
and so on...
So now it seems I have to create a handler for all the possible routes:
router.post('/contact_form', function(req, res, next) {
// ...
}
router.post('/route1/contact_form', function(req, res, next) {
// ...
}
How can I easily handle POST requests from all the routes they may be coming from without writing a handler for each?
You can use absolute path reference in your form and it will always submit to the same route even though the form is in different pages.
Try this
form(method="POST" action="/contact_form")
Notice the action changed from contact_form to /contact_form. When you add /, you start referencing the path as an absolute path to the domain. So now, from all pages, the form will be submitted to http://your-domain/contact-form.
Not entirely sure if this is what you mean, but the first argument to ExpressJS's router (I assume that's what router is doing here) can be an array. So instead of:
router.post('/contact_form', function(req, res, next) {
// ...
}
router.post('/route1/contact_form', function(req, res, next) {
// ...
}
You can just do:
router.post(['/contact_form','route1/contact_form'],function(req,res,next){
//some fancy logic to handle both routes.
})
Of course, this requires that you keep a list of these possible routes. On the other hand, you can follow Dinesh Pandiyan's advice, and just use an absolute path. So instead of page1.html, page2.html, page3.html, etc. all having their own own router (or own entry in your router array), you'd essentially be saying "Go to the domain route, then go to this address".
Each request should be handled in separated functions because each request has its own logic. However if you want
function request(req, res, next) {
// Your logic
}
router.post('/contact_form', request) {
// ...
}
router.post('/route1/contact_form', request) {
// ...
}
Right now, I don't have a way to test this code, but I think that will help you.
Here is yet another potential solution - use an independent function as a route handler.
router.post('/a', handlePost);
router.post('/b', handlePost);
router.post('/c', handlePost);
function handlePost(req, res, next){
// use req.path here to figure out what url was called
}
I inherited a codebase where it looks like they run middleware in node with the following pattern for Oauth2 passport strategy
module.exports = function (router) {
router.get('/', function (req, res, next) {
passport.authenticate('type', object, function(err, info) {
//pass info object to next middleware
})(req,res,next) <---where does this go?!?
})
}
From my current understanding of the code base, this is actually the last function call in the middleware chain, so could I just add a piece of middleware to the bottom?
Does this sound like the right idea?
And just to clarify what I'm trying to do:
pass data from Oauth callback through middleware function by attaching it to the req
perform DB business logic (create or lookup account)
login with JWT
redirect
This appears to be the "custom callback" method of using passport's authenticate function. If you look at the documentation you can see how they expect it to be used. That said, I don't know what that second argument is supposed to be doing (the object) - it looks like a variable, but I don't see it defined anywhere, and I'm not sure the authenticate method takes arguments in that manner. Also, the custom callback takes three arguments: err, user, and then info... which might trip you up.
Okay, so now to your actual question of "could I just add a piece of middleware to the bottom?" Sort of? The fact is, you're in a routing middleware at that point. If it matches and auth is successful, then you should do whatever code for that route is required inside the custom callback. That's the point of this way of doing things. Alternatively you could use passport.authenticate as a piece of middleware itself (it returns a middleware function usable in the CommonJS pattern.
If you don't want to change up the code, then you could just do this:
module.exports = function (router) {
router.get('/', function (req, res, next) {
passport.authenticate('PICK A VALID TYPE', function(err, user, info) {
// this custom callback will be executed once auth completes
// (either successfully or not
// put code in here to perform DB business logic, login, and redirect
})(req,res,next); <--- this executes the passport.authenticate middleware
})
};
I've been working on creating a better architecture for rest api in express and node. Let's say I have 3 methods in my route middleware -
router.post('/users/:id', [
UserService.getUserById,
UserController.setUser,
MailerService.sendSubscriptionMail
]);
I am setting up req.session.user in call to UserService.getUserById and then using that to set req.session.result in UserController.setUser. Now I am sending a mail to this user using the data stored in req.session.result.
UserService -
exports.getUserById = function(req, res, next) {
.
.
req.session.user = data;
.
.
};
module.exports = exports;
UserController -
exports.setUser = function(req, res, next) {
.
.
req.session.result = req.session.user;
.
.
};
module.exports = exports;
MailerService -
exports.sendSubscriptionMail = function(req, res, next) {
// Using req.session.result here to send email
};
module.exports = exports;
Now I have two questions regarding above process -
(a) Is there any chance that a new http req to another route (which also has these kind of methods which can modify req.session) can modify the req.session.result and MailerService.sendSubscriptionMail does not get the data which it needs to send to the user or will that req object will be completely different from this one in the memory?
(b) Is there any other method to transfer data between middleware rather than setting up req object?
Is there any chance that a new http req to another route (which also
has these kind of methods which can modify req.session) can modify the
req.session.result and MailerService.sendSubscriptionMail does not get
the data which it needs to send to the user or will that req object
will be completely different from this one in the memory?
The req object is specific to this request. That object cannot be changed by another request. But, if the session in req.session is a common shared sesison object that all requests from that particular user share, then req.session.result could be changed by another request from that user that is getting processed at around the same time (e.g. interleaved within various async operations).
If you want to make sure that no other request from this user could change your result, then put it in req.result, not req.session.result because no other requests will have access to req.result.
Is there any other method to transfer data between middleware rather
than setting up req object?
The req or res objects are the right places to share info among middleware handlers for the same request as they are unique to this particular request. Be careful with the session object because it is shared among multiple requests from the same user.
Another possible way to share the data among your three handlers is to make a single middleware handler that calls all three of your middleware handlers and then share the data within that single function (e.g. passing it to the other handlers).
For example, you could change the calling signature of your 2nd two methods so you can get the data out of setUser() and then pass it directly to sendSubscriptionMail() without using the req or session objects to store it.
router.post('/users/:id', [UserService.getUserById, function(req, res, next) {
UserController.setUser(req, res, function(err, result) {
if (err) return next(err);
MailerService.sendSubscriptionMail(result, req, res, next);
}]);
});
Is there any way to shift data from a request in a POST function to a resource of a GET function?
app.get '/form', routes.getForm
app.post '/form', routes.postForm
app.get '/form', routes.getForm # pass return data from previous POST call
I need to validate some form data and would like to preset data that has been entered before the POST.
You shouldn't do it that way, instead you should be using a middleware function, for example:
validate = (req, res, next) ->
# validate your req.body stuff here
...
if data_is_valid
next() # proceed to the next function
else
res.redirect '/form?data=invalid'
app.post '/form', validate, routes.getForm