I used express-generator to generate the basic structure of an express app.
I have this in routes/my.js:
router.use('/update', function(req, res, next) {
req.headers['content-type'] = 'application/json';
console.log('router use invoked');
next();
});
router.get('/update', function(req, res, next) {
console.log('=== GET /my/update', req.body);
});
router.post('/update', function(req, res, next) {
console.log('=== POST /my/update', req.body);
});
And in app.js I added:
var my = require('./routes/my');
app.use('/', routes);
app.use('/users', users);
app.use('/my', my);
It's working fine for GET, and the router middle-ware is actually working (console.log is called), but the headers is not being set to app/json (as the log message in post outputs the body to be empty).
I can solve this by adding this before any other middle-ware
app.use('/my/update', function(req, res, next) {
req.headers['content-type'] = 'application/json';
next()
});
And it works. I tried moving the app.use('my', my); line before any other middle-ware but it stopped working altogether.
What can I do in order to give priority to my router.use?
You're adding the header to the request object, not the response. Also the below is the preferred way to do it.
router.use('/update', function(req, res, next) {
res.header('content-type', 'application/json');
console.log('router use invoked');
next();
});
ExpressJS is all about working with middle wares.
According to official documentation here, Middleware is a function with access to the request object (req), the response object (res), and the next middleware in line in the request-response cycle of an Express application, commonly denoted by a variable named next.
Middleware can:
Execute any code
Make changes to the request and the response objects
End the request-response cycle
Call the next middleware in the stack
I would like to point a few things about your my.js router, if you don't want to send any response back then it should be something like :
router.get('/update', function(req, res, next) {
res.end();
});
router.post('/update', function(req, res, next) {
res.end();
});
You must end your request, response cycle else it will be left hanging. Practically you would like to send some data(preferably JSON data) back to client so you can use res.json() or res.send(). You don't need to set application/json headers if you use res.json().
Related
I'm doing a web app with nodeJS and for the authentication I'm using PassportJs with passport-local-mongoose.
I make this snippet for the root route to check if the user is logged in
app.get('/', function(req, res){
if(req.isAuthenticated()){
List.find({}, function(err, results){
if(!err){
res.render('home');
}});
}else{
res.redirect('/login');
}
});
so my question is: there's a way to redirect the user to the login page if they are non-logged, from all URLs without specifying it in all the routes get.
Express comes up with middlewares. Basically, a request pass through all middlewares that match its path and http method. You can do whatever you want in a middleware, then either send a response or pass the request to the next middleware.
// All get requests
app.get('*', function(req, res, next){
// If not authenticated
if(!req.isAuthenticated()) {
res.redirect('/login');
}
// Else, go to the next middleware
next();
}
// Real business here
app.get('/', function(req, res){
List.find({}, function(err, results){
if(!err){
res.render('home');
}});
}
});
You can read more here : https://expressjs.com/en/guide/using-middleware.html
This part of code caused the problem to my app.
router.use(function(req, res, next){
if(req.user){
res.locals.username = req.user.username
}else{
res.redirect('/login');
}
next();
});
router.get('/', function(req, res, next) {
res.render('dashboard');
});
If I removed the first block, everything worked fine. But the first part have to be there, to act as the auth middleware. But it crashed and I got error of below:
_http_outgoing.js:341
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
Try returning immediately after performing the redirect() to avoid further route handler executions that may also try to set headers. You will also want to check req.url before redirecting. For example:
router.use(function(req, res, next){
if (req.user) {
res.locals.username = req.user.username
} else if (!/\/login/i.test(req.url)) {
return res.redirect('/login');
}
next();
});
This will prevent execution from continuing if you are redirecting the request (redirect() sends (the appropriate) headers).
There are 2 weak points in the code you provided:
It redirect() the request and then continue processing it as if nothing happened. It should stop processing the request, in other words not call next() after redirect, as #mscdex correctly pointed out.
It always redirects the user to the /login page if the user is not provided. Even if the user is requesting /login, creating endless cycles of redirects: /login -> [no user] -> [redirect /login] -> /login -> [no user] -> ...
The most common pattern to handle user authorization is this:
// middleware to check user authorization
function checkAuth(req, res, next){
if(req.user){
res.locals.username = req.user.username
next(); // authorization handled successfully, continue processing request
}else{
res.redirect('/login');
// finish processing request without calling next()
}
}
// we add checkAuth middleware to prevent unauthorized users from accessing server root ('/')
router.get('/', checkAuth, function(req, res, next) {
res.render('dashboard');
});
// but we don't need to check user authorization on '/login'
router.get('/login', function(req, res, next) {
res.render('...');
});
I've got a NodeJS + Express Server setup with a router that looks like this:
app.route('/clients/:clientId)
.get(users.ensureAuthenticated, clients.read)
.put(users.ensureAuthenticated, clients.hasAuthorization, clients.update)
.delete(users.ensureAuthenticated, clients.hasAuthorization, clients.delete);
app.param('clientId', clients.clientByID);
My Problem is that users.ensureAuthenticated fills the req parameter with the current user req.user.
Basically it does this: req.user = payload.sub; (with some other background stuff)
Then the req.user is available in the following functions e.g. clients.update, but not in clients.clientByID.
I know I could execute users.ensureAuthenticated in clients.clientByID again, but this would execute the code twice and be extra load on the server, right? I guess there must be another way, but I couldn't find anything in the documentation of express.
I'd like to know how I can access the req.user in clients.clientByID without executing the code in users.ensureAuthenticated twice.
Based on your question, I assume you would like to execute users.ensureAuthenticated before clients.clientByID is executed. This can be achieved by using the app.use functionality. app.use handlers will get executed before the app.param and app.route handlers.
For example:
var express = require('express');
var app = express();
app.use('/user', function(req, res, next) {
console.log('First! Time to do some authentication!');
next();
});
app.param('id', function(req, res, next, id) {
console.log('Second! Now we can lookup the actual user.');
next();
});
app.get('/user/:id', function(req, res, next) {
console.log('Third! Here we do all our other stuff.');
next();
});
app.listen(3000, function() {
});
Building a REST API with NodeJs and Express: my goal is to implement the 2 simplest Middlewares:
The first one is going to log the incoming request
The second one is going to log the processed response (which is a JSON)
The first middleware (in app.js)
function logReqMiddleware(req, res, next) {
log.info('>>> %s at %s', req.method, req.path);
next();
}
The second middleware (in app.js)
function logResponseMiddleware(req, res, next) {
log.info('<<< %s (%s)', JSON.stringify(res), res.status);
next();
}
The controller function (in apiController.js)
export var router = express.Router();
router.get('/', foo);
function foo(req, res, next) {
res.status(200).json({ stupidExample: true });
next();
}
Building the app (app.js)
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(logReqMiddleware);
app.use('/api', apiController.router);
app.use(logResponseMiddleware);
var server = app.listen(3000, function(){ log.info('running'); });
Now, making a request at localhost:3000/api says: can't set headers after they are sent; this is the related super cool response my question is, is it incompatible to write json data with json() and adding the second middleware? How can I solve this?
Don't call next() inside logResponseMiddleware. There's nothing else to do, so calling next() is probably sending you into express's default 404 handler which is trying to respond with a default status and body.
Aside: JSON.stringify(res) is not what you want. res is an object instance representing the response (lots of methods, internal state properties, etc). It is not the response body content string. As the res is a writeable stream, it's not going to keep the response body content around for you to log, it's just going to try to send it over the HTTP connection.
I'm new to using Passport.js, but I find it's going pretty well so far. I'm using Passport with passport-local.
However, I want to require authentication for an entire directory excluding one page. So in my node server I'm serving up this direcory like so (using express):
app.use("/admin", express.static(__dirname + "/admin"));
And then I want to let the user hit /admin/login.html, so I wanted to do something like this:
app.get('/gb-admin/login.html', function(req, res){ });
Then I want to require authentication for the rest, so something like this:
app.get('/gb-admin/*', ensureAuthenticated, function(req, res){});
Here is my ensureAuthenticated function, for reference if it helps:
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/gb-admin/login.html')
}
How would I go about doing this? I've been generally sending things in infinite loops and causing the browser to timeout. Can anyone help?
The reason you're getting timeouts is because you can't have an empty route handler; at one point, you have to either return a response, or hand the request over the the next route handler/middleware.
That said, try this:
function ensureAuthenticated(req, res, next) {
if (req.path === '/gb-admin/login.html' || req.isAuthenticated()) {
return next();
}
res.redirect('/gb-admin/login.html')
}
app.get('/gb-admin/*', ensureAuthenticated, function(req, res, next) {
next();
});
// the static middleware needs to be declared after the route above, otherwise
// it will take precedence and ensureAuthenticated will never be called.
app.use("/gb-admin", express.static(__dirname + "/admin"));
I don't think there's a way to get it working with a separate route for the login page (unless you actually implement reading login.html and sending it back from without that routes handler), hence the check for it in the ensureAuthenticated middleware.
I wonder if it is your callback. Try:
app.get('/gb-admin/*', function (req, res, next) {
ensureAuthentication(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/gb-admin/login.html')
});
});