How to delegate route processing in express? - node.js

I have expressjs application with straitfort route processing like the following:
app.route('/').get(function(req, res, next) {
// handling code;
});
app.route('/account').get(function(req, res, next) {
// handling code;
});
app.route('/profile').get(function(req, res, next) {
// handling code;
});
At now I put all my code inside route handler but I want try to delegate it to some class such as the following.
app.route('/').get(function(req, res, next) {
new IndexPageController().get(req, res, next);
});
app.route('/account').get(function(req, res, next) {
new AccountPageController().get(req, res, next);
});
app.route('/profile').get(function(req, res, next) {
new ProfilePageController().get(req, res, next);
});
So what do you thing about the approach above and meybe you know the better one?

As you can see in the Express Response documentation - the response (req) can send information to the client by a few methods. The easiest way is to use req.render like:
// send the rendered view to the client
res.render('index');
Knowing this means that you can do whatever you want in another function, and at the end just call res.render (or any other method that sends information to the client). For example:
app.route('/').get(function(req, res, next) {
ndexPageController().get(req, res, next);
});
// in your IndexPageController:
function IndexPageController() {
function get(req, res, next) {
doSomeDatabaseCall(function(results) {
res.render('page', results);
}
}
return {
get: get
}
}
// actually instantiate it here and so module.exports
// will be equal to { get: get } with a reference to the real get method
// this way each time you require('IndexPageController') you won't
// create new instance, but rather user the already created one
// and just call a method on it.
module.exports = new IndexPageController();
There isn't strict approach to this. You can pass the response and someone else call render. Or you can wait for another thing to happen (like db call) and then call render. Everything is up to you - you just need to somehow send information to the client :)

Related

I cant use a function returning another function in a express js helper

I have this helper :
function loginRequired(msg){ // this is the helper
return function(req, res, next){
if (req.user.is_authenticated){ // example
return next()
}
//else
req.flash('error_msg',msg)
return res.redirect('/')
}
}
and I have this route ( I`m using the helper in this route ) :
router.post('/new', loginRequired(msg='You are not allowed here'), async (req, res)=>{
// code
})
The problem is that the function returned by the helper is not being executed, when I request this route, it keeps loading forever and the content is never sent.
There isn't anything obviously wrong with the loginRequired() function and how you're using it so the problem is probably elsewhere. The only thing I see is that if req.user doesn't exist, then it would throw.
For further debugging, I would suggest you do this to make sure your route is being matched.
function logRoute(msg) {
return function(req, res, next) {
console.log(msg);
next();
}
}
router.post('/new', logRoute("/new handler"), loginRequired(msg='You are not allowed here'), async (req, res)=>{
// code
});
And, make sure you see /new handler in the console. If you don't even see that, then the problem is further upstream with how your route is declared as it isn't matching an incoming request. You would have to show us the rest of that code for us to see how the router is being used.

Is there any way to monitor every response in Express.js?

I want to create a middleware function in express.js. which can monitor every requests and responses. I created a middleware but it can only monitor the requests, not the responses.
function middlewareFunc (req,res,next) {
console.log(req.body , req.params , req.query);
next();
}
You should know that res in function(req, res, next) is a instance of class http.ServerResponse. So it can be listen on finish event, please see the link: https://nodejs.org/api/stream.html#stream_event_finish
app.use(function (req, res, next) {
function afterResponse() {
res.removeListener('finish', afterRequest);
res.removeListener('close', afterRequest);
// action after response
}
res.on('finish', afterResponse);
res.on('close', afterResponse);
// action before request
// eventually calling `next()`
});
app.use(app.router);
app.use() and middleware can be used for "before" and a combination of the close and finish events can be used for "after."
For that you can write two middlewares
1) Before all request endpoints.
//middleware
function middlewareFunEarlier(req,res,next) {
console.log(req.body , req.params , req.query);
next();
}
app.use(middlewareFunEarlier);
app.get('/', function(req, res, next){
//do something
res.end();
});
2) After all end points. And you must have to use next() in all endpoints
app.get('/', function(req, res, next){
//do something
next();
});
app.use(middlewareFunLater);
//middlware
function middlewareFunLater(req, res, next){
console.log(res);
res.end();
}
It can be work around with existing tools.
Ok, so first of all, the reason you are only seeing the requests is because of how middleware works. Everything gets run once in a certain order, and runs only once. When your middleware gets run it is most likely before the response has been created. In order to get the response you would have to make your code run when your controller goes to render or something like that.
Second of all, it seems like basic logging is all you need.(weather it be with a library or just console logging stuff.)

Log response status in sails

I'm trying to log every request made to my sails application, but I can't find a way to log the response associated with a request.
I added this custom middleware in the config/http.js file :
myRequestLogger: function (req, res, next) {
req.on("end", function(){
sails.log(res.statusCode);
});
return next();
}
But it doesn't work properly, I can get the 200 codes, buta res.forbidden or res.notFound response is not logged. Any idea about how I could handle that ?
Thank you
You can override that in api/responses itself. Here is simplified override:
// api/responses/forbidden.js
module.exports = function(err, viewOrRedirect) {
// ... Sails logic
this.req._sails.log.verbose();
}
But, if you expect that your middleware above can do this, you're wrong. Your middleware should looks similar to this:
myRequestLogger: function(req, res, next) {
req._sails.log.verbose('YOUR LOG');
return next();
}
Ok, I have found the answer by reading this stackoverflow post : https://stackoverflow.com/a/11841877/2700309
Apparently there is a 'finish' event emitted just before the response is send to the client. So the right code would be :
myRequestLogger: function (req, res, next) {
res.on("finish", function(){
sails.log(res.statusCode);
});
return next();
}
And this seems to work!

forwarding to another route handler without redirecting in express

I have the following code :
app.get('/payment', function(req, res) {
// do lots of stuff
});
now I want to add the following :
app.post('/payment', function(req, res) {
req.myvar = 'put something here';
// now do the same as app.get() above
});
Obviously I want to reuse the code. I tried doing next('/payment') inside the post handler and put it above the get handler, but no luck, probably because they are different VERBs.
What are my options ?
Thanks.
Just lift out the middleware to its own function and use it in both routes.
function doLotsOfStuff (req, res) {
// do lots of stuff
}
app.get('/payment', doLotsOfStuff);
app.post('/payment', function(req, res, next) {
req.myvar = 'put something here';
next();
}, doLotsOfStuff);

Forward request to alternate request handler instead of redirect

I'm using Node.js with express and already know the existence of response.redirect().
However, I'm looking for more of a forward() functionality similar to java that takes the same parameters as redirect, but internally forwards the request instead of having the client perform the redirect.
To clarify, I am not doing a proxy to a different server. I'd like to forward('/other/path') directly within the same app instance
It wasn't apparently obvious how to do this from the express documentation. Any help?
You just need to invoke the corresponding route handler function.
Option 1: route multiple paths to the same handler function
function getDogs(req, res, next) {
//...
}}
app.get('/dogs', getDogs);
app.get('/canines', getDogs);
Option 2: Invoke a separate handler function manually/conditionally
app.get('/canines', function (req, res, next) {
if (something) {
//process one way
} else {
//do a manual "forward"
getDogs(req, res, next);
}
});
Option 3: call next('route')
If you carefully order your router patterns, you can call next('route'), which may achieve what you want. It basically says to express 'keep moving on down the router pattern list', instead of a call to next(), which says to express 'move down the middleware list (past the router)`.
You can implement forward (aka rewrite) functionality by changing request url property and calling next('route').
Note that the handler performing forward needs to be configured before other routes which you perform forwards to.
This is example of forwarding all *.html documents to routes without .html extension (suffix).
function forwards(req, res, next) {
if (/(?:.+?)\.html$/.test(req.url)) {
req.url = req.url.replace(/\.html$/, '');
}
next('route');
}
You call next('route') as the last operation. The next('route') passes control to subsequent routes.
As mentioned above, you need to configure forwards handler as one of the first handlers.
app.get('*', forwards);
// ...
app.get('/someroute', handler);
The above example will return the same content for /someroute as well as /someroute.html. You could also provide an object with a set of forward rules ({ '/path1': '/newpath1', '/path2': '/newpath2' }) and use them in forward mechanism.
Note that regular expression used in forwards function is simplified for mechanism presentation purposes. You would need to extend it (or perform check on req.path) if you would like to use querystring parameters etc.
I hope that will help.
For Express 4+
Using the next function does not work if the next handler is not added in the right order. Instead of using next, I use the router to register the handlers and call
app.get("/a/path", function(req, res){
req.url = "/another/path";
app.handle(req, res);
}
Or for HTML5 mode of React/Angular
const dir = process.env.DIR || './build';
// Configure http server
let app = express();
app.use('/', express.static(dir));
// This route sends a 404 when looking for a missing file (ie a URL with a dot in it)
app.all('/*\.*', function (req, res) {
res.status(404).send('404 Not found');
});
// This route deals enables HTML5Mode by forwarding "missing" links to the index.html
app.all('/**', function (req, res) {
req.url = 'index.html';
app.handle(req, res);
});
Using the next function does not work if the next handler is not added in the right order. Instead of using next, I use the router to register the handlers and call
router.get("/a/path", function(req, res){
req.url = "/another/path";
router.handle(req, res);
}
Express 4+ with nested routers
Instead of having to use the outside of route/function app, you can use req.app.handle
"use strict";
const express = require("express");
const app = express();
//
// Nested Router 1
//
const routerOne = express.Router();
// /one/base
routerOne.get("/base", function (req, res, next) {
res.send("/one/base");
});
// This routes to same router (uses same req.baseUrl)
// /one/redirect-within-router -> /one/base
routerOne.get("/redirect-within-router", function (req, res, next) {
req.url = "/base";
next();
});
// This routes to same router (uses same req.baseUrl)
// /one/redirect-not-found -> /one/two/base (404: Not Found)
routerOne.get("/redirect-not-found", function (req, res, next) {
req.url = "/two/base";
next();
});
// Using the full URL
// /one/redirect-within-app -> /two/base
routerOne.get("/redirect-within-app", function (req, res, next) {
req.url = "/two/base";
// same as req.url = "/one/base";
//req.url = req.baseUrl + "/base";
req.app.handle(req, res);
});
// Using the full URL
// /one/redirect-app-base -> /base
routerOne.get("/redirect-app-base", function (req, res, next) {
req.url = "/base";
req.app.handle(req, res);
});
//
// Nested Router 2
//
const routerTwo = express.Router();
// /two/base
routerTwo.get("/base", function (req, res, next) {
res.send("/two/base");
});
// /base
app.get("/base", function (req, res, next) {
res.send("/base");
});
//
// Mount Routers
//
app.use("/one/", routerOne);
app.use("/two/", routerTwo);
// 404: Not found
app.all("*", function (req, res, next) {
res.status(404).send("404: Not Found");
});
app.get('/menzi', function (req, res, next) {
console.log('menzi2');
req.url = '/menzi/html/menzi.html';
// res.redirect('/menzi/html/menzi.html');
next();
});
This is my code:when user enter "/menzi",the server will give the page /menzi/html/menzi.html to user, but the url in the browser will not change;
You can use run-middleware module exactly for that. Just run the handler you want by using the URL & method & data.
https://www.npmjs.com/package/run-middleware
For example:
app.runMiddleware('/get-user/20',function(code,body,headers){
res.status(code).send(body)
})

Resources