I have this middleware that check every routes for user session. This just simply do if user logged in, go to next function.
router.use(function(req, res, next){
if(!req.user){
res.redirect('/login');
}else{
res.locals.username = req.user.username;
return next();
}
});
But how to exclude it for certain route? For example I want to do a public API within my app also. Like example.com/api/items should not check for user login session.
req.originalUrl tells you what the URL was so you can decide to skip your logic by examining the URL right in your middleware function.
Or, you could set up a separate router for the public path that was processed before this router so if the request matched the public path router, then it would already be handled and thus this middleware in a later router wouldn't be called. That is how Express expects you to have different middleware for different top level paths - by using separate routers.
Related
Express js middleware is not working as expected. It is showing too many redirections.
When i remove the token or logout it shows in the browser that too many redirections
Middleware
const isAuthenticate = async (req, res, next) => {
const token = req.cookies.jwt;
if (token) {
jwt.verify(token, "thisisjwtsecret", async (err, token_decode) => {
if (!err) {
const u_id = token_decode._id;
const userData = await User.findOne({ _id: u_id });
req.user = userData;
req.isAuth = true;
next();
} else {
res.redirect("/user/login");
}
});
} else {
res.redirect("/user/login");
}
};
Route.js
// Auth Controller
const AuthController = require("../../controllers/auth/AuthController");
const { isAuthenticate } = require("../../middlewares/isAutheticated");
router.get("/user/login", isAuthenticate, AuthController.login);
router.post("/user/login", AuthController.checkLogin);
router.get("/user/register", isAuthenticate, AuthController.createUser);
router.post("/user/register", isAuthenticate, AuthController.storeUser);
module.exports = router;
LOgin function
// Showing Login Page to User
const login = (req, res) => {
return res.render("auth/login");
};
When i remove the token or logout it shows in the browser that too many redirections
Now that you've shown revised code for isAuthenticate(), the redirect loop is caused by the redirects in that code. Here's what happens:
Some route you have (any route) that uses isAuthenticate as middleware for the route detects that the user is not logged in. It then redirects to /user/login. That's fine up to that point. Then, the browser issues a new request for /user/login and that takes you to this route definition:
router.get("/user/login", isAuthenticate, AuthController.login);
But, that route definition again runs the isAuthenticate() middleware which redirects to /user/login and thus you have an infinite redirect loop.
Probably you just need to remove the isAuthenticate() check from this route. If the user is already going to the /user/login page, you don't need to check their authentication or redirect them. If you have a reason to want to know if they are authenticated or not, then you need a separate version that ONLY does the auth check and does not redirect and you can use that in the /user/login route definition.
Original answer before code was shown that did res.redirect().
So, this middleware you show sets req.isAuth to true or false and then calls next() to continue routing. All three code paths through that middleware just set req.isAuth and then call next(). Nowhere in this middleware does it do any redirect. So, if the core problem is too many redirections, that issue must be caused somewhere else by some other route/middleware that actually does a redirect, probably when it sees that req.isAuth is false since you said that the problem occurs when logged out or when the token is missing.
When redirecting, you have to make absolutely sure that when you redirect to a URL, there is ZERO chance (no code path of any kind) that the route handler for that URL will also redirect to that URL. That's how you get into a redirect loop.
Looking at the other routes you show, if the too many redirects issue is when redirecting to /user/login, then it seems likely the problem is in the authController.login() handler from this route:
router.get("/user/login", isAuthenticate, AuthController.login);
If the code for that route checks req.isAuth and redirects in any circumstances, then that would be an endless redirect loop.
If you need further advice, please provide the following information:
Which exact redirect URL is causing the problem of too many redirects? Is is /user/login?
Show us the code for the route that does that redirect because that's apparently where the fault is.
I have an app that enables users to define their own routes on the fly. And I still want to display an custom 404 message, so in the last middleware I do this:
app.use((req, res, next) => {
// ...normal logic
// check for user defined routes
next()
// if res is not written, i.e. request not processed by user defined routes
if (!res.writableEnded) {
res.status(404).send(`not found!`)
}
})
However, I got
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
. How can I implement this?
There is no way for middleware to know if a subsequent route handler is going to pick up the path.
You need to define it last, really last, after any user defined routes.
Then you need to not call next().
Example code:
app.use('/list-product', (req, res, next) => {
// Do stuff
next();
})
app.use('/add-product', (req, res, next) => {
console.log("I'm here")
})
Somehow it doesn't log "I'm here'", which means next() call doesn't work.
But if I change '/add-product' to '/' or I remove it at all, it works. Why is that?
How can I jump to the next middleware which has initial url on it as the example above?
It sounds like you don't quite understand that when you put a path in front of the middleware, the URL must a least be a partial match for that path for that middleware to be called at all.
So, when you do a request for /list-product, that will match your /list-product middleware which will then call next(). That will continue on the middleware chain looking for other middleware handlers that match the /list-product path. When it gets to your next middleware for /add-product, that doesn't match /list-product so it is skipped (not called).
If you change app.use('/add-product', ...) to app.use('/', ...) or to app.use(...) with no path, then those last two are matches for /list-product so they will get called. The / middleware is a partial match (for all URLs) and middleware with no path will be called for all URLs. app.use() runs for partial matches. The more specific request handlers like app.get() and app.post() require a more complete match.
I have following routes in nodejs
here is users.js route
router.post('/users/login', function(request, response) {
// let user login here and do all of the logic.
// Once done then redirect user to dashboard in dashboard.js file
response.redirect('/dashboard');
});
here is dashboard.js route
router.get('/dashboard', function(request, response) {
response.render('plans');
});
Now when I login as a user when it tries to redirect me it says following.
cannot get Cannot GET /users/dashboard
I want users' to redirect to /dashboard/dashboard instead.
How can I redirect users from one route to another in Express?
I had the same doubt as you had, I came here to look for the answer but found none so I had to do some digging in the docs myself for the solution, and I found one.
Using res.redirect('/dashboard') is taking to the path which is relative to the current route i.e users/dashboard, if you have a separate route for dashboard then you'll have to use it this way:
res.redirect('../dashboard/dashboard')
Just for reference here is my project structure:
routes/
> index.js
> user_home.js
> users.js
Here are the contents of user_home.js:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.send("Hello User, welcome to user_home page");
});
router.get('/dashboard', function(req, res, next) {
res.send("Hello User, this your personal dashboard");
});
module.exports = router;
and here is the code from the route from where i am redirecting :
else {
console.log("Input validated!");
res.redirect('../user_home/dashboard');
}
res.redirect('auth/login');
I had this in one of my route named 'contributions' and when it is redirected, it is rendered as 'contributions/auth/login' which is wrong.
The main thing i needed was 'localhost://port/auth/login.
i changed my ejs anchor tag to '/auth/login' which will take url from the root, It previously was 'auth/login' which took the url as current url + provided url, i.e., 'contributions/auth/login' in my case.
Hope this helps.
Don't do response.redirect('/dashboard'); with a / in it, as this will add /dashboard to the current url. Do this instead: response.redirect('dashboard');.
in my case I've created a redirect in case I don't have a req.query parameter. So after the redirect I added a return.
if(req.query.tk==null)
{
res.redirect('landing-page');
return;
}....
Yup I am just 5 years late anyway, you are redirecting from a 'get' request to a 'post' request. That is why you are getting this error
First of all, i have searched the solution to this problem and i didn't found anything. Sorry if it's duplicated.
I have in my express+node.js app two endpoints like this:
// Gets a tweet by unique id
app.get('/tweets:id', function(req, res, next) {
// Response management
});
// Gets mentions of user unique id
app.get('/tweets/mentions', function(req, res, next) {
// Response management
});
The problem is that requesting a GET petition to "/tweets/mentions", is attended first by "/tweets/:id" and later by "/tweets/mentions", making a conflict.
I have tried to change the declaration order of the endpoints, but always the request is attended by both endpoints.
Also I have tried things like "/tweets::mentions", but I need to access the endpoint via "/tweets/mentions", and I suppose there is a possible way.
How can i resolve this conflict?
Thanks.
Are you using next() in one of the handlers?
next() passes control to the next matching route, so in your example, if one of them is called and inside it you call next(), the other one will be called.
I allways recommend to use 'Router' if you have more than one base path because it helps you to keep it organized.
You can resolve the conflict by checking the value of req.params.id in the "tweet by id" handler.
For routes with additional parameters is always recommended to not use the same base path of other routes.
Something like could work for you:
app.get('/tweets/users/:id', function(req, res, next) {
// Response management
});
// Gets mentions of user unique id
app.get('/tweets/mentions', function(req, res, next) {
// Response management
});