I have some routes under /users that I want to require an authentication token and some that I do not. In order to achieve this I have done the following.
var protected = express.Router();
var unprotected = express.Router();
unprotected.post('/', function(req, res, next) { // A
// no auth required
next();
});
protected.get('/', function(req, res, next) { // B
// auth required
next();
});
module.exports = {
protected: protected,
unprotected: unprotected
};
And then I mount those routes in the following way.
var users = require('./routes/users');
app.use('/users', unprotected_middleware, users.unprotected); // C
app.use('/users', protected_middleware, users.protected); // D
app.use(resultHandler); // Middleware for sending the response (all routes)
app.use(errorHandler); // Middleware for handling errors (all routes)
When I make a POST to /users it runs the expected route, however when next() gets called the protected_middleware runs. This happens because it finds the next /users definition at the line marked with 'D'.
What is the recommended way to get around this?
For each resource I want the ability to have different middleware per route.
POST /users (no auth)
GET /users (requires auth)
DELETE /users/{id} (requires auth and admin access)
You don't need two separate routers here, simply use the middlewares for protected and unprotected where needed (in the router get/post functions):
users.js:
var router = express.Router();
router.post('/', unprotected_middleware,
function(req, res, next) { // A
// no auth required
next();
}
);
router.get('/', protected_middleware,
function(req, res, next) { // B
// auth required
next();
}
);
module.exports = router;
and then:
var users = require('./routes/users');
app.use('/users', users);
app.use(resultHandler); // Middleware for sending the response (all routes)
app.use(errorHandler);
Related
I am trying to allow users to save and download static files. But have the files be accessible only for logged in (authorized) users.
routes/files.js
var express = require('express');
var router = express.Router();
/* Get files */
router.get('/*', isLoggedIn, function(req, res, next) {
console.log('get files called ');
express.static(path.join(__dirname, 'files'));
});
function isLoggedIn(req, res, next){
if(req.isAuthenticated()) return next();
res.redirect('/');
}
module.exports = router;
This works with any get request but not static files. They can be accessed with out being logged in.
app.js
var filesRouter = require('./routes/files');
app.use(express.static(__dirname));
app.use('/files', filesRouter);
I must be missing something simple - Thanks!!
Calling express.static(path.join(__dirname, 'files')); just returns a middleware function. It doesn't actually execute anything more than that. It doesn't serve any files until you call the function that it returns. You could get that middleware function once and then execute it conditionally, but it's easier to just do this:
/* Get files */
router.get('/*', isLoggedIn, express.static(path.join(__dirname, 'files')));
You should use express.static as a middleware.
And then to only allow authenticated users to access files, you coulld register isLoggedIn middleware before it. Please , see the code bellow, this should work.
var express = require('express');
var router = express.Router();
router.use(isLoggedIn); // this must come before express.static
router.use(express.static(path.join(__dirname, 'files')));
/* Get files */
router.get('/*', function(req, res, next) {
console.log('get files called ');
});
function isLoggedIn(req, res, next){
if(req.isAuthenticated()) return next();
res.redirect('/');
}
module.exports = router;
In app.js I have:
const routes = './routes/'
const usersRouter = require(routes +'users');
/*more code*/
app.use('/users', usersRouter);
In users.js, I have:
const express = require('express');
const router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
module.exports = router;
My question: How does router.get('/') know to get the users file? Usually I would need to pass '/users' to router.get(. But with app.use('/users' in app.js, all I need is router.get('/') in users.js. In fact if I type router.get('/users' in users.js the program breaks.
Learning Express, and I could use explanation of how this works.
You have register the usersRouter as a middleware which will be executed when the Request URI is prefixed with /users an example will be https://localhost:3000/users/ or http://localhost:3000/users/subroute
// attach `usersRouter` as middleware
app.use('/users', usersRouter);
If in your users.js you have define route like this
router.get('/', (req, res) => {});
router.post('/users', (req, res) => {});
router.get('/:userId', (req, res) => {});
router.delete('/:userId', (req, res) => {});
module.exports = router;
You'll have this list of routes
GET => http://localhost:3000/users
POST => http://localhost:3000/users/users/
GET => http://localhost:3000/users/1
DELETE => http://localhost:3000/users/1
If the Request URI which you are trying to access is define in your express app, the handler will fallback to the Express buildin Error Handler.
So if you have define a router router.get('/users', ...) in the users.js file the application won't break as long you are trying to reach that router with http://...:../users/users.
Learn more about middleware
Learn more about Express routing
When you do this:
app.use('/users', usersRouter);
That already filters to send ONLY requests that start with /users to the router. It also strips off the /users from the start of the path so the router itself will do matching on the reduced path that has /users stripped from it which for /users will just be /. For a URL like /users/something, the router will match against /something.
Thus router.get("/", ...) will match the original URL of /users, but not /users/something.
The line
app.use('/users', usersRouter);
will make the users router handle requests prefixed with /users. The router in the users file therefore handles not /, but /users because you told your app to use that particular router for /users/*. In reality, this is simply a shortcut of doing router.get('/users') instead.
I'm having a problem when adding middleware in express.Router().
let router = express.Router();
let mid = function(req, res, next) {
console.log("mid");
next();
}
router.get("/", function(req, res) {
res.send("public");
})
let userRouter = express.Router();
userRouter.use(mid);
userRouter.get("/users", function(req,res) {
res.send("users");
})
router.use(userRouter);
app.use("/", router);
router = express.Router();
router.get("/", function(req, res) {
res.send("api");
})
app.use("/api", router);
and here is the result:
accessing "/" don't triggers the middleware.
accessing "/users" triggers the middleware.
accessing "/api" triggers the middleware.
I don't get why "/api" is triggering the middleware.
Check the Express documentation for router middleware.
The Express API documentation might be useful too.
I organized your code a little bit, and now it is working as you wanted.
'use strict';
let express = require('express');
let expressApp = express();
// --- middleware function ---
let usersMiddleware = function(req, res, next) {
console.log("users router middleware");
next();
}
// --- middleware function ---
let appMiddleware = function(req, res, next) {
console.log("app middleware");
next();
}
// --- public router ---
let publicRouter = express.Router();
publicRouter.get("/", function(req, res) {
res.send("public");
});
// --- user router ---
let userRouter = express.Router();
// --- this is a router based middleware,
// --- to be used only for this router ---
userRouter.use(usersMiddleware);
userRouter.get("/", function(req,res) {
res.send("users");
});
// --- api router ---
let apiRouter = express.Router();
apiRouter.get("/", function(req, res) {
res.send("api");
});
// --- this is a app based middleware,
// --- it will be used for every route of the app after this line
expressApp.use(appMiddleware);
// --- adding the routers to the app ---
expressApp.use("/", publicRouter);
expressApp.use("/users", userRouter);
expressApp.use("/api", apiRouter);
expressApp.listen(8080);
Note that there are two kinds of middleware used here, the one attached to an specific router (that's the one you wanted), and one attached to the app, used for all the further routes (I added this one to show the difference).
/api was triggering the middleware because you added it to the app and not to the router instance.
I'm building a example website using Express and I hit something I don't quite understand.
Error-handling middleware(s) should be last one(s) in the pipeline, if I understand correctly. For example, works just fine:
var http = require('http');
var express = require('express');
var app = express();
app.set('view engine', 'jade');
app.set('views', './views');
app.use(express.static('./public'));
http.createServer(app).listen(portNumber, function() { });
app.get('/hello', function(req, res) {
res.send('Welcome!');
});
app.use(function(err, req, res, next) {
res.status(500).send('something broke!');
});
app.get('/error', function(req, res, next) {
somethingNonExistent(2016);
});
However, if I register that middleware before http.createServer call, but after all other middlewares were registered, it won't work - my code isn't called:
var http = require('http');
var express = require('express');
var app = express();
app.use(express.static('./public'));
app.use(function(err, req, res, next) {
res.status(500).send('something broke!');
});
http.createServer(app).listen(portNumber, function() { });
app.get('/hello', function(req, res) {
res.send('Welcome!');
});
app.get('/error', function(req, res, next) {
somethingNonExistent(2016);
});
What did I miss here? My guess is that app.get calls use some middleware internally and it gets messed up. I use Express 3.2.6 and Node.js 0.10.29, if that makes any difference
When you define routes/middleware, the path you specify is used to see if it matches with incoming requests. Your request will always be routed to the first match. A request might have multiple matches, so order matters here. You can hit the next matching route/middleware by calling the next() function.
When you mount middleware using app.use without specifying a path, every path is eligible to hit that middleware. So, if it's the first thing you mount, every request will use that middleware.
If you want a catch all error handler, you'll want the opposite - you should mount middleware at the very end of your route definitions. You'll need to call the next function in your handler to actually reach this middleware:
app.get('/hello', function(req, res, next) {
...
// Let's pretend that there was some error
next()
});
// After all of your route definitions...
app.use(function(req, res) {
res.status(500).send('something broke!');
})
Note that if no route exists for the current path, you will also hit this catch all middleware.
Docs
http://expressjs.com/en/guide/error-handling.html
You define error-handling middleware last, after other app.use() and
routes calls; for example:
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
app.use(bodyParser());
app.use(methodOverride());
app.use(function(err, req, res, next) {
// logic
});
The Internal
For ease of understanding, you can imagine the pipeline. When your controller receives a request, inside Express, it looks like this
try {
fn(req, res, next); // your controller
} catch (err) {
next(err);
}
So, then your code throw error, it will call next with err. Basically, it's the same as call next(new Error()). After that, express trying to find next middleware with 4 arguments in middleware pipeline. In your situation error handler declared before your shady controller, so he doesn't involved.
Technically, there is no difference between the controller and middlewares. Optionally you can pass in the controller next parameter , and call it to pass through the pipeline further. If you don't call next(), you finish processing the request.
i am trying to execute below code after upgrading express4
// call the Router
var router = express.Router();
router.get('/test1', function(req, res, next) {
// doing more stuff
res.send('test test1 route')
});
// call our router we just created
app.use('/dogs', dogs);
but for some reason i am getting following error
this._router = new Router({
^
TypeError: object is not a function
at Function.app.lazyrouter
can someone help me to solve this problem ,Thank you in advance.
From the documentation:
var app = express();
app.route('/events')
.all(function(req, res, next) {
// runs for all HTTP verbs first
// think of it as route specific middleware!
})
.get(function(req, res, next) {
res.json(...);
})
.post(function(req, res, next) {
// maybe add a new event...
})
So try:
var router = app.route();
router.get('......
Or check the 3 to 4 upgrade guide.
Which says:
app.router has been removed and middleware and routes are executed in
the order they are added. Your code should move any calls to app.use
that came after app.use(app.router) after any routes (HTTP verbs).
app.use(cookieParser());
app.use(bodyParser());
/// .. other middleware .. doesn't matter what
app.get('/' ...);
app.post(...);
// more middleware (executes after routes)
app.use(function(req, res, next);
// error handling middleware
app.use(function(err, req, res, next) {});
I don't have a reputation to comment. Where and how did you declare your dog? Did you mean the following?:
// call the Router
var router = express.Router();
// call our router we just created
app.use(router);
router.get('/test1', function(req, res, next) {
// doing more stuff
res.send('test test1 route')
});
we can use this approach for routing in express4, when i am upgrading to express4 i didn't deleted express 3 folder, i tried deleting express3 folder from node modules and installing express4 than this worked fine