router in express.js base on parameters - javascript

I have these router declared in express.js, I wonder why the run user got triggered when I open localhost:3000/myname/profile.
router.get('/:username', function(req, res, next)
{
console.log('run user')
});
router.get('/:username/profile', function(req, res, next)
{
console.log('run user profile')
});
I expect it won't,how to solve that?
please anyone help me?
Thank you in advance....

Just rearrange the code as shown below and your code should work fine.
router.get('/:username/profile', function(req, res, next)
{
console.log('run user profile')
});
router.get('/:username', function(req, res, next)
{
console.log('run user')
});
The issue is with the order in which the routes are defined, Since both the routes have /:username when you hit http://localhost:3000/myname/profile., the first route is given preference since it matches the uri.
Also refer this stackoverflow post on express route naming and ordering -
Node.js Express route naming and ordering: how is precedence determined?

Are you sure about that? I tried your code and it triggered run user profile.May you should show the all code.

Related

Hot reloading with express and chokidar causes a http headers sent error when using multiple routes

I've been trying a variety of setups for hot reloading and one that I've come across is the https://github.com/glenjamin/ultimate-hot-reloading-example/. Modifying this boilerplate code as a starting point, I've come across the following problem in my server code:
// server.js
import chokidar from 'chokidar';
import express from 'express';
const app = express();
// this is the middleware for handline all of my routes
app.use(function (req, res, next) {
require('./server/index')(req, res, next);
// if I commented out any additional routes, the setup would work fine
require('./server/foo')(req, res, next);
require('./server/catch-all')(req, res, next);
});
//this watches the server folder for changes
const watcher = chokidar.watch('./server');
watcher.on('ready', function () {
watcher.on('all', function () {
console.log("Clearing /server/ module cache from server");
Object.keys(require.cache).forEach(function (id) {
if (/[\/\\]server[\/\\]/.test(id)) delete require.cache[id];
});
});
});
app.listen(3000, 'localhost', function (err) {
if (err) throw err;
const addr = this.address();
console.log('Listening at http://%s:%d', addr.address, addr.port);
});
The above is the server code that handles clearing the cache by watching for changes with the chokidar module. If I have just one route required inside the app.use middleware function (which listens for every incoming request), I can get it to work. However if have multiple routes, the following error occurs:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
This is a common issue posted on stack overflow, but all of the solutions I've come across and attempted haven't worked. My route files are as follows:
//index.js
import express from 'express';
const router = express.Router();
router.get('/', (req, res, next) => {
res.send("greagrehgarhegrehuh").end();
return next('router');
});
module.exports = router;
//end of index.js
//foo.js
import express from 'express';
const router = express.Router();
router.get('/foo', (req, res, next) => {
res.send("foo").end();
return next('router');
});
module.exports = router;
//end of foo.js
//catch-all.js
import express from 'express';
const router = express.Router();
router.get('*', (req, res, next) => {
res.send("catch all").end();
return next('router');
});
module.exports = router;
// end of catch-all.js
All three routes do the same thing, bar the endpoint. So far I've explicitly called end on each to end the response, used return next('router') to skip the rest of the middleware functions and have also tried doing it without the above as well. Any ideas on what I'm missing here to get this working? Here's a github project that showcases the issue
https://github.com/RonanQuigley/express-chokidar-hot-reload
UPDATE
So I actually removed the next calls and seem to have almost got it working by doing the following:
app.use(function (req, res, next) {
require('./server/index')(req, res, next);
require('./server/foo')(req, res, next);
});
// a second app.use middleware, that does the same
// as the catch all // * router.get from my original post
app.use(function (req, res, next) {
app.get('*', (req, res) => res.send('catch all'));
})
However, I can't use this second app.use with another require call to a file with an express router like the others. So it seems that express runs through the middleware stack, reaches the * and tries to set the header twice.
The reason I need the * is normally if a user requests an endpoint that doesn't exist, Node correctly shows up with cannot GET/. However, for some reason, with the setup I've outlined express will then crash. My workaround is using * at the end of the middleware stack and I'd just use a res.redirect to send the user back to wherever, but this causes the above issue I've outlined in my original post. So not sure how to get around that one.
So currently I have either:
1) Hot reloading works without the require for a router.get('*'), but when the user navigates to an endpoint that doesn't exist, express will crash.
2) Hot reloading works with the app.get('*') inside a second app.use call, but I can't then use a router to move this into a separate file.
Okay, so posting this solution up for my own future reference and in case somebody else stumbles into this problem.
After speaking with the express devs, it turns out that this is indeed possible with a combination of the following:
// you need to use comma separated routes
app.use(
dynamic('./server/index'),
dynamic('./server/foo')
);
// require the library at runtime and apply the req, res, next arguments
function dynamic(lib) {
return function (req, res, next) {
return require(lib).apply(this, arguments)
}
}
In the case of webpack, this would break it as you can't use require as an expression. So use the following to get around that:
function createRoutes(router) {
const dynamic = (lib) => {
return function (req, res, next) {
// let webpack generate a regex expression from this require
// if we don't you would get a critical dependency warning
// which would result in the routes not being found
return require("./src/" + lib + ".js").apply(this, arguments);
}
}
router.use(
dynamic('index'),
dynamic('foo'),
);
return router;
}
Let's step back a bit and talk about middleware.
Say you have a function which runs some kind of middleware.
const runMiddleware = (req, res, next) => {
console.log(`this will run everytime a HTTP request comes in`);
}
Then to use that middleware within express:
app.use(runMiddleware);
Every time any (GET, POST, DELETE, etc) request comes in, this function is run.
Essentially you are doing the same thing below - You are wrapping three (3) route calls with a single function. This function is calling all of these routes at once, hence res is actually being sent 3 times in a row in the example below:
app.use(function (req, res, next) { // runs every time any request comes in
require('./server/index')(req, res, next); // res sent, ok
require('./server/foo')(req, res, next); // res sent, err
require('./server/catch-all')(req, res, next); // res sent, err
});
Here is a basic way of handling routes:
const index = require('./server/index');
const foo = require('./server/foo');
app.use('/', index);
app.use('/foo', foo);
// catch everything else
app.use(function (req, res) {
res.send('catch all');
})

Express.js require session to access particular routes

I have an app where certain pages require that the user be logged in.
I am not sure if there is something built in for this, but what I have for doing this is as follows:
app.use((req, res, next) => {
if (req.session.username) {
app.get('/project/create', projectCtrl.create)
app.get('/project/create/save', projectCtrl.save)
} else {
return res.redirect('/')
}
next()
})
Is this the correct way of doing this, or is there a better way in express? The way I am doing it kind of feels a little hacky.
Yes, that's one correct way of doing it. What you have is an application-level middleware in express. It gets called for every request the application receives.
You can extract the username check and apply that as a route middleware substack. This way the middleware only gets executed for the routes it's applied to.
function gatePass(req, res, next) {
if(req.session.username) {
next();
}
else {
return res.redirect('/');
}
}
app.get('/project/create', gatePass, projectCtrl.create)
app.get('/project/create/save', gatePass, projectCtrl.save)
You can take this a bit further if you'll like to separate concerns by using express router together with route-level middleware. This also applies a middleware directly to the routes.
var router = express.Router();
router.use('/project/create', gatePass);
router.use('/project/create/save', gatePass);
router.get('/project/create', projectCtrl.create);
router.get('/project/create/save', projectCtrl.save);
app.use('/', router);
this solution work. It's not the best but for small project it will be good. The only drawback is that you will need to define every route you want to be check with a session.
Nodejs is the world of middleware, so why not use one? I think it's the best thing to do.
Verify is a file where I export my middleware and I apply it on all my router.. (in this case it's just to check if the user is logged or not)
var verify = require('./verify');
router.all('/*', verify.isLogged, function(req, res, next) {
if(req.decoded._doc.isLogged == "") {
next();
}
else {
res.json("error");
}
});
This way, if in the future you need to check one thing, then another one, you will just need to call you function where you want to check
router.get('/test', verify.isLogged, verify.isAdmin function(req, res, next) {
if(req.decoded._doc.isAdmin == "") {
next();
}
else {
res.json("error");
}
});

Express Routing - Root Page and 404

I am working on a Node app that uses Express. In this app, I have the following:
app.use('/', function(req, res) {
res.render('index', {});
});
This route works fine for the positive case. However, it fails for the negative case. For example, If I visit "http://www.example.com/404", I still see the index page. In reality, I want it to fall through so that the Express Error handler tackles the error.
How do I change my route so that when a person visits the root of my app, they see the home page. yet, if they enter anything else, they'll see the 404 page?
You want to use app.get() (or possibly app.all()), not app.use():
app.get('/', function(req, res) {
res.render('index', {});
});
The reason why app.use('/', ...) matches /404 is explained here:
A route will match any path that follows its path immediately with a “/”. For example: app.use('/apple', ...) will match “/apple”, “/apple/images”, “/apple/images/news”, and so on.
You can define middleware to specifically handle errors.
Express error-handling middleware takes four arguments and should be defined after all your other routes:
app.use('/', function(req, res) {
res.render('index', {});
});
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});

How to obtain the next route's path in an Express app

I am catching all traffic before passing it forward using:
app.all('*', function(req, res, next) {
... run before stuff, related to the next req.route.path
next();
});
and I want to run some code before calling the next() function.
in order for me to know the proper code I need to run, I have to identify what is the next request route path.
Debugging current req object (inside all('*',.. ) does not giving any information about the next request.route.path
How can I get the next method route.path before calling it?
Your help will be appreciated. Thank you.
Instead of trying to look ahead, why not explicitly set middleware for the routes that need it?
var middleware = function (req, res, next) {
..run your code in here
};
app.get('/users:user_id', middleware, function(req, res, next) {
});
You can get the next route by checking the route when the response in the middleware has fired the finish event:
app.all('*', function(req, res, next) {
res.on('finish', function() {
console.log('Next route: ', req.route.path);
});
next();
});
For a route defined like this:
app.get('/users/:user_id', function(req, res) {
res.send('Hello');
});
You'll obtain the log:
$ Next route: '/users/:user_id'

Express.js: Use variables in route's path

I'm developing a web application with MEAN stack and I want this scenario:
People can sign up and after that, They must have at least one company registered.
When they register a company, the base URL will be like these:
example.com/companyName/
example.com/companyName/members
example.com/companyName/settings
example.com/companyName/etc
So, my problem is, how can I use variables in my routes to match the user's registered company name?
Is it something like app.use('/'+companyName, routes); possible in app.js?
There is a piece of my code, just in case:
app.use(function (req, res, next) {
console.log('going to '+companyUrl);
next();
});
app.use('/'+companyUrl, routes);
I'm doing some works on other modules, and now, noStartupRestrict.theStartupUrl is companyName as it should be.
But the app is not using routes. it goes all the way down to 404 route.
Try this
app.use('/:companyName/something', function(req, res, next) {
console.log(req.params.companyName);
next();
})
Did you try?
app.js
...
var path = '/companyName';
app.use(path, routes);
...

Categories

Resources