I want my logger middleware to log each matched route when response is sent. But there may be any number of nested subroutes. Let's suppose I have this:
var app = express();
var router = express.Router();
app.use(function myLogger(req, res, next)
{
res.send = function()
{
//Here I want to get matched route like this: '/router/smth/:id'
//How can I do this?
});
}
app.use('/router', router);
router.get('/smth/:id', function(req, res, next)
{
res.send(response);
});
Is it possible?
Because app-level middleware has no knowledge of routes, this is impossible. However, if you use your logger middleware as route middleware like:
router.get('/smith/:id', logger, function (req, res) { ... });
You can use a combination of two parameters on the request object:
req.route.path => '/smth/:id'
req.originalUrl => '/router/smth/123'
I'll leave it up to you how you want to combine both into one string.
Here's the code (in express 2.x)
// npm -v express
// 2.14.2
var app = express();
var router = express.Router();
app.use(function(req, res, next) {
var routes = app.routes; // See Reference 1.
for(var method in routes) {
if(routes.hasOwnProperty(method)) {
for(var route in routes[method]) {
if(req.url.toString().match(routes[method][route].regexp)) {
console.log('Route Debugger: ' + routes[method][route].path);
}
}
}
}
next();
});
app.use('/router', router);
router.get('/smth/:id', function(req, res, next)
{
res.send(response);
});
What does this do?
It queries the app.routes object. Now we have access to all the routes defined in our application.
We match the current url req.url with the regular expression of each route.
Since this is a application level middleware, it runs for every request. So you get logging like Route Debugger: /router/smth/:id, if you hit a url like /router/smith/123
Reference 1 : http://thejackalofjavascript.com/list-all-rest-endpoints/
Reference 2 : How to get all registered routes in Express?
Related
For my NodeJs project I use the Express router. I created a router.js
module.exports = (app) => {
app.use('/', require('./routes/home.js'));
app.use('/contact', require('./routes/contact.js'));
app.use('/imprint', require('./routes/imprint.js'));
};
and in my app.js I require this file require('./server/router.js')(app);. When calling a route, for example /contact I require the contact.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.render('contact', { // render contact.hbs
title: 'Contact' // pass in some template variables
});
});
module.exports = router;
Within router.get I can use console.log(req.method) to log the HTTP method. Currently I would have to log this in all router files. Is it possible to have the code at one place like
module.exports = (app) => {
console.log(req.method); // Log the request method for all routes
app.use('/', require('./routes/home.js'));
app.use('/contact', require('./routes/contact.js'));
app.use('/imprint', require('./routes/imprint.js'));
};
Please provide a solution without installing middleware. I know there are many loggers out there.
If you want to avoid installing middleware and use console.log you can change your code as follows:
module.exports = (app) => {
app.use((req, res, next) => {
console.log('Request Type:', req.method);
console.log('Time:', Date.now());
next();
})
app.use('/', require('./routes/home.js'));
app.use('/contact', require('./routes/contact.js'));
app.use('/imprint', require('./routes/imprint.js'));
};
If you don't specify a route but have the next callback in, then it will execute your logging code first then the matching routing if there is one. You can find more details in the using middleware section of the ExpressJS API documentation.
What I want to do
if req is from mobile
first check these routes
router.use('/', mobileRoutes);
then ones below
router.use('/', routes);
but as far as I understand express goes through all use statements upon every request, it runs them all once and put route handlers into some sort of array. that get hit with requests.
For some urls I want to server different pages but no all (loginPage, registrationPage, etc).
Is it possible to do conditional routing, to prepend more routes in case some condition is met.
What I do have working is this:
router.use(function (req, res, next) {
let md = new MobileDetect(req.headers["user-agent"]);
if (md.mobile()) {
req.url = '/mobile'+req.url;
}
next();
});
//mobile routes
router.use("/mobile", require("./mobile"));
but it completely blocks all routes below.
I want a clean solution, One another option, which is not clean, is to add middleware to individual routes but that muddies code endlessly.
My Solution for conditional routing:
index.js
.....
//mobile routes
router.use("/", require("./mobile"));
router.get("/", require("./get"));
.....
mobile.js
const express = require("express");
const router = express.Router();
const MobileDetect = require("mobile-detect");
router.use(function (req, res, next) {
let md = new MobileDetect(req.headers["user-agent"]);
(md.mobile())? next() : next("router")
});
router.get("/", function (req, res) {
res.send({ query: req.query });
});
module.exports = router;
what I want it to do.
router.post('/xxxx', authorize , xxxx);
function authorize(req, res, next)
{
if(xxx)
res.send(500);
else
next();
}
I want to check for session in each route.
But since the routers are written in this way.
router.route('/xxx/xxxx').post(function(req, res) {
// blah lah here...
//
});
So how can I set up a middleware that will check for session and I wanted to make things a bit more generic and wanted to have a single authorize function doing a single thing instead of checking in every request.Any suggestions.
Define a middlware function before you define / include your routes, this will avoid you checking for a valid session in every route. See code below for an example on how to do this.
If some routes are public, i.e. they do not require a user to have a valid session then define these BEFORE you 'use' your middlware function
var app = require("express")();
//This is the middleware function which will be called before any routes get hit which are defined after this point, i.e. in your index.js
app.use(function (req, res, next) {
var authorised = false;
//Here you would check for the user being authenticated
//Unsure how you're actually checking this, so some psuedo code below
if (authorised) {
//Stop the user progressing any further
return res.status(403).send("Unauthorised!");
}
else {
//Carry on with the request chain
next();
}
});
//Define/include your controllers
As per your comment, you have two choices with regards to having this middleware affect only some routes, see two examples below.
Option 1 - Declare your specific routes before the middleware.
app.post("/auth/signup", function (req, res, next) { ... });
app.post("/auth/forgotpassword", function (req, res, next) { ... });
//Any routes defined above this point will not have the middleware executed before they are hit.
app.use(function (req, res, next) {
//Check for session (See the middlware function above)
next();
});
//Any routes defined after this point will have the middlware executed before they get hit
//The middlware function will get hit before this is executed
app.get("/someauthorisedrouter", function (req, res, next) { ... });
Option 2 Define your middlware function somewhere and require it where needed
/middleware.js
module.exports = function (req, res, next) {
//Do your session checking...
next();
};
Now you can require it wherever you want it.
/index.js
var session_check = require("./middleware"),
router = require("express").Router();
//No need to include the middlware on this function
router.post("/signup", function (req, res, next) {...});
//The session middleware will be invoked before the route logic is executed..
router.get("/someprivatecontent", session_check, function (req, res, next) { ... });
module.exports = router;
Hope that gives you a general idea of how you can achieve this feature.
Express routers have a neat use() function that lets you define middleware for all routes. router.use('/xxxxx', authorize); router.post('/xxxx', 'xxxx'); should work.
Middleware:
sampleMiddleware.js
export const verifyUser = (req, res, next) => {
console.log('Verified')
next();
}
Routes
import express from 'express';
import { verifyUser } from './sampleMiddleware.js';
const userRoutes = express.Router();
userRoutes.route('/update').put(verifyUser, async function(){
//write your function heere
});
You've probably gotten the answer you need but I'll still drop this
router.route('/xxx/xxxx').get(authorize, function(req, res) {...});
I want to just verify something but have't been able to find anything in the Express docs or online regarding this (although I know it's a feature).
I could just test this out but I don't really have a nice template and would like to hear from the community.
If I define a route in express like such:
app.get('/', function (req, res) {
res.send('GET request to homepage');
});
I can also define a middleware and load it directly, such as
middleware = function(req, res){
res.send('GET request to homepage');
});
app.get('/', middleware)
However, I can also chain at least one of these routes to run extra middleware, such as authentication, as such:
app.get('/', middleware, function (req, res) {
res.send('GET request to homepage');
});
Are these infinitely chainable? Could I stick 10 middleware functions on a given route if I wanted to? I want to see the parameters that app.get can accept but like mentioned I can't find it in the docs.
Consider following example:
const middleware = {
requireAuthentication: function(req, res, next) {
console.log('private route list!');
next();
},
logger: function(req, res, next) {
console.log('Original request hit : '+req.originalUrl);
next();
}
}
Now you can add multiple middleware using the following code:
app.get('/', [middleware.requireAuthentication, middleware.logger], function(req, res) {
res.send('Hello!');
});
So, from the above piece of code, you can see that requireAuthentication and logger are two different middlewares added.
It's not saying "infinitely", but it does say that you can add multiple middleware functions (called "callbacks" in the documentation) here:
router.METHOD(path, [callback, ...] callback)
...
You can provide multiple callbacks, and all are treated equally, and behave just like middleware, except that these callbacks may invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to perform pre-conditions on a route then pass control to subsequent routes when there is no reason to proceed with the route matched.
As you can see, there's not distinction between a middleware function and the function that commonly handles the request (the one which is usually the last function added to the list).
Having 10 shouldn't be a problem (if you really need to).
Express version "express": "^4.17.1" or above
From the document: Series of Middleware
var r1 = express.Router();
r1.get('/', function (req, res, next) {
next();
});
var r2 = express.Router();
r2.get('/', function (req, res, next) {
next();
});
app.use(r1, r2);
Let's try a real life example:
tourController.js
exports.checkBody = (req, res, next)=>{ // middleware 1
if (!req.body.price){
return res.status(400).json({
status:'fail',
message:'Missing price!!!'
})
}
next();
}
exports.createTour = (req, res) => { // middleware 2
tours.push(req.body);
fs.writeFile(
`${__dirname}/dev-data/data/tours-simple.json`,
JSON.stringify(tours),
(err) => {
res.status(201).json({
status: 'success',
data: {
tour: newTour,
},
});
}
);
};
tourRouter.js
const express = require('express');
const tourController = require('./../controller/tourController')
const router = express.Router();
router.route('/')
.get(tourController.getAllTours)
.post(tourController.checkBody, tourController.createTour);
//muliple Middleware in post route
module.exports = router //need this or the following step will break
app.js
const express = require('express');
const tourRouter = require('./route/tourRouter');
const app = express();
app.use(express.json());
app.use('/api/v1/tours', tourRouter);
module.exports = app;
suppose I have a simple express js application like the following:
var express = require('express');
var app = express();
app.get('/', function(req, res) {
return res.json({ hello: 'world' });
});
module.exports = app;
I want to be able to go to the command line, require the app, start the server and simulate a request. Something like this:
var app = require('./app');
app.listen(3000);
app.dispatch('/') // => {hello:"world"}
You can use run-middleware module exactly for that. This is working by creating new Request & Response objects, and call your app using those objects.
app.runMiddleware('/yourNewRoutePath',{query:{param1:'value'}},function(responseCode,body,headers){
// Your code here
})
More info:
Module page in Github & NPM;
Examples of use run-middleware module
Disclosure: I am the maintainer & first developer of this module.
These two options worked for me without any error:
option 1
app.post('/one/route', (req, res, next) => {
req.url = '/another/route'
req.method = 'GET'
next();
});
app.get('/another/route', (req, res) => {
console.log("Hi, I am another route");
});
option 2
app.post('/one/route', (req, res, next) => {
req.url = '/another/route'
req.method = 'GET'
app._router.handle(req, res, next);
});
app.get('/another/route', (req, res) => {
console.log("Hi, I am another route");
});
Express : 4.15.4
No extra library or npm module is required
this solution works perfectly by using express 4.16
(and optionally -express promise router , which is great wrapper for error handling)
its straightforward, without using router inside a router nor re-write the request, like in the other suggested answers
just change the URL in the request and return it to router handle function
const router = require('express-promise-router')();
router.post('/signin', function(req, res , next) {
if (req.body.Username === 'admin') {
req.url = '/admin/signin'
router.handle(req, res, next)
}
else { // default doctor
req.url = '/doctors/signin'
router.handle(req, res, next)
}
});
router.post('/doctors/signin',someCallback1)
router.post('/admin/signin',someCallback1)
As far as I am aware, there isn't a way to switch to a specific route internally, but there is a way to flag the request, then move on to the next route:
app.use((req, res, next) => {
if("nextRouteCondition"){
req.skip = true;
return next();
}
})
This may allow you to accomplish what you want to do.