I am using express and I want to have my user profile URLs like this: example.com/:username
However, I still need other URLs such as example.com/login and example.com/view/:id
If I order the router like this, it treats "login" as a username when a request is sent to example.com/login:
router.get('/:username', function (req, res, next) {
res.render('profile', {data: req.params.username});
})
router.get('/login', function (req, res, next) {
res.render('login', {data: null});
})
router.get('/view/:id', function (req, res, next) {
res.render('view', {data: req.params.id});
})
If I put the /:username router at the end, everything works correctly. However, if someone went to example.com/view (without an id), I need it to send an error that the view controller didn't receive an id. Instead, it sees it as a username again and instead sends an error that the username doesn't exist.
What is the cleanest way to solve this? Do I just have to add a router for all base url paths? (Something like this):
router.get('/login', function (req, res, next) {
res.render('login', {data: null});
})
router.get('/view/:id', function (req, res, next) {
res.render('view', {data: req.params.id});
})
router.get('/view', function (req, res, next) {
res.render('viewError', {data: null});
})
router.get('/:username', function (req, res, next) {
res.render('profile', {data: req.params.username});
})
I am not entirely sure if this is the right way to do it, but then again this sounds like something I might encounter and I would like it to be solved by this method until I find a better solution.
The below solution uses a single route for the path format /:value, be it login, view or any username, hence you could put in a simple if-else-if or switch to give control to respective controllers or just simply render a view from it. This way the order in which it has to be handled doesn't matter at all.
router.get("/:username", function(req, res, next) {
if (req.params.username === "login") {
res.render("login", { data: null });
} else if (req.params.username === "view") {
res.render("viewError", { data: null });
} else {
res.render("profile", { data: req.params.username });
}
});
router.get("/view/:id", function(req, res, next) {
res.render("view", { data: req.params.id });
});
Related
Currently, I have the following code for many more oath provider:
// facebook
router.get("/facebook", passport.authenticate("facebook", { scope: ["email"] }));
router.get("/facebook/callback", passport.authenticate("facebook"), (req, res) => {
console.log(chalk.blue("went into facebook callback"));
res.redirect("http://localhost:3000/profile");
});
// github
router.get("/github", passport.authenticate("github"));
router.get("/github/callback", passport.authenticate("github"), (req, res) => {
console.log(chalk.blue("went into github callback"));
res.redirect("http://localhost:3000/profile");
});
Is there a way to unify that into an abstracted route? I.e. something like
// github
router.get("/:provider", passport.authenticate(:provider));
router.get("/:provider/callback", passport.authenticate(:provider), (req, res) => {
console.log(chalk.blue("went into {:provider} callback"));
res.redirect("http://localhost:3000/profile");
});
Update:
The following piece of code does what I want. Thx to #Usman Abdur Rehman.
function callbackDistributer(req, res, next) {
console.log(req.params);
global.provider = req.params.provider;
next();
}
router.get(
"/:provider/callback",
callbackDistributer,
(req, res, next) => {
passport.authenticate(global.provider)(req, res, next);
},
(req, res) => {
console.log(chalk.red("went into: " + global.provider));
res.redirect("http://localhost:3000/profile");
}
);
Have a middleware function going before the passport.authenticate middleware
function ownMiddleware(req,res,next){
global.provider = req.params.provider
next()
}
and then use it in the route handler as
router.get("/:provider/callback", ownMiddleware ,passport.authenticate(global.provider), (req, res) => {
console.log(chalk.blue("went into {:provider} callback"));
res.redirect("http://localhost:3000/profile");
});
I think it should work
I'm building/learning a web-app with React and Express. All of the routes and redirects work but URL won't change and my props won't pass until i manually go to the URL.
For example;
After a successful login (local passport with MongoDB), it renders main page but it's empty since i don't get any data (user id or email etc..) but if enter URL manually or press home button on nav-bar, it works or if i logout it logouts but URL stays at /logout instead of /login. Example code below:
server.js
...
server.use((req, res, next) => {
res.locals.success_msg = req.flash("success_msg");
res.locals.error_msg = req.flash("error_msg");
res.locals.error = req.flash("error");
res.locals.messages = req.flash();
res.locals.user = req.user;
next();
});
server.get("/index", ensureAuthenticated, (req, res) => {
const msg = {name: req.user.name, email: req.user.email};
return app.render(req, res, "/index", msg);
});
server.post("/login", (req, res, next) => {
passport.authenticate("local", function(err, user, info) {
if (err) {
return next(err);
} else if (!user) {
req.flash("error_msg", info.message);
return app.render(req, res, "/login", req.flash());
} else {
req.logIn(user, function(err) {
if (err) {
return next(err);
}
req.user = user.name;
return app.render(req, res, "/index", user.name);
});
}
})(req, res, next);
});
server.get("/logout", (req, res) => {
req.logOut();
req.flash("success_msg", "done!");
return app.render(req, res, "/login", req.flash());
});
server.get("*", ensureAuthenticated, (req, res) => {
return handle(req, res);
});
I think that what you meant by return app.render(req, res, "/index", user.name); on your login method, is actually a redirect.
What render does is take the file and the data you give it and then send it back to the browser as a response.
However, what you're trying to do is have the user go to a different URL if the login process is successful, that can be accomplished by doing the following:
res.redirect('/index')
This will make the server go to your index route, which in turn executes all the code required for your user data to be loaded!
You can learn more about redirect and render by looking at the express docs.
I am using NodeJS, Express and Handlebars (template engine) to build a web application. Currently I'm trying to automatically redirect users whenever they enter an URL that does not exist (or whenever they might not have access to it).
The following returns the index page:
router.get('/', (req, res) => {
res.render('index/index');
});
But how do I make something like this:
router.get('/:ThisCouldBeAnything', (req, res) => {
res.render('errors/404');
});
The following example is from Github:
Say that I enter this URL:
https://github.com/thispagedoesnotexist
It automatically returns a 404. How do I implement this in my application?
Thanks in advance.
Use a middleware just after all route handlers to catch non existing routes:
app.get('/some/route', function (req, res) {
...
});
app.post('/some/other/route', function (req, res) {
...
});
...
// middleware to catch non-existing routes
app.use( function(req, res, next) {
// you can do what ever you want here
// for example rendering a page with '404 Not Found'
res.status(404)
res.render('error', { error: 'Not Found'});
});
After all your other routes you can add:
app.get('*', (req, res) => {
res.render('errors/404');
});
Alternately, you can use a middleware function after all your other middleware and routes.
app.use((req, res) => {
res.render('errors/404');
});
So you might end up with something that looks like:
//body-parser, cookie-parser, and other middleware etc up here
//routes
app.get('/route1', (req, res) => {
res.render('route1');
});
app.get('/route2', (req, res) => {
res.render('route2');
});
//404 handling as absolute last thing
//You can use middleware
app.use((req, res) => {
res.render('errors/404');
});
//Or a catch-all route
app.get('*', (req, res) => {
res.render('errors/404');
});
I see that you have express tagged. All you have to do is include a default handler that includes
res.status(404).render('404template')
For example
app.get('*', (req, res,next) => {
res.status(404).render('error.ejs')
});
My view relies on some data to render, like this:
router.get("/", (req, res) => {
res.render(
'index', {
data: layout_data,
user: req.user
}
);
});
router.post('/login', passport.authenticate('local'), function(req, res) {
res.redirect('/');
});
It works. But when I added the csurf middleware like
router.post('/login', parseForm, csrfProtection, passport.authenticate('local'), function(req, res) {
res.redirect('/');
});
The layout engine tells me that data is undefined. I suspect that the csurf middleware erases the data.
I tried to fix it like this:
router.get("/", (req, res) => {
req.mydata = {};
req.mydata.layout_data = layout_data;
res.render(
'index', {
data: req.mydata.layout_data,
user: req.user
}
);
});
router.post('/login', parseForm, csrfProtection, passport.authenticate('local'), function(req, res) {
if (!req.mydata) {
req.mydata = {};
}
req.mydata.layout_data = layout_data;
res.redirect('/');
});
It still does not work. The data is still undefined.
What is the best practice to resolve this problem?
My guess is: your layout_data is undefined.
I want to do something like this. I want to use different middleware if there is or isn't a certain query string.
app.get("/test?aaa=*", function (req, res) {
res.send("query string aaa found");
});
app.get("/test", middleware, function (req, res) {
res.send("no query string");
});
However, I failed. Can anyone help me? Thanks.
EDIT: I only need to add the middleware, I dont care what the value of the query string is
If your intention is to run the same route handler and call the middleware depending on whether the query string matches, you can use some sort of wrapping middleware:
var skipIfQuery = function(middleware) {
return function(req, res, next) {
if (req.query.aaa) return next();
return middleware(req, res, next);
};
};
app.get("/test", skipIfQuery(middleware), function (req, res) {
res.send(...);
});
If you want to have two route handlers, you could use this:
var matchQueryString = function(req, res, next) {
return next(req.query.aaa ? null : 'route');
};
app.get("/test", matchQueryString, function (req, res) {
res.send("query string aaa found");
});
app.get("/test", middleware, function (req, res) {
res.send("no query string");
});
(these obviously aren't very generic solutions, but it's just to give an idea on how to solve this)
You can do this:
app.get("/test", middleware, function (req, res) {
res.send("no query string");
});
middleware = function(req, res, next) {
if(!req.query.yourQuery) return next();
//middleware logic when query present
}