URL rewriting not working in Express 4.14.1 - javascript

I've found in quite a few SO posts that in order to rewrite a URL in Express 4 I would do something like the following:
router.use('/one/:someId', (req, res, next) => {
req.url = `/two/${req.params.someId}`;
next();
});
router.get('/one/:someId', (req, res) => {
res.send("reached /one/:someId");
});
router.get('/two/:someId', (req, res) => {
res.send("reached /two/:someId");
});
But when I try this, not only does the URL does not change to my expected "/two/some integer" and stays being "/one/some integer" but it gets to the 404 - Not Found page I have set up in my app file.
This routes are in a router file and I have also tried setting the URL to:
req.url = `/routerPath/two/${req.params.someId}`;
but the result is exactly the same.
So what could I be missing?
Thank you.

You have to distinguish two kinds of redirects:
Internal redirects work on the server, without the client noticing. They are a convenience for your server programming and never necessary - you could always introduce a helper method that gets called by all endpoints.
HTTP redirects advise the client (e.g. a web browser) to go to a different URL. Since you expect the URL to change, that's the one you want.
Simply call res.redirect, making sure to encode special characters:
router.get('/one/:someId', (req, res) => {
res.redirect(`/two/${encodeURIComponent(req.params.someId)}`);
});
router.get('/two/:someId', (req, res) => {
res.render("reached /two/:someId");
});

Related

Does app.use(express.static("public")) call the middleware for every request?

Does using app.use(express.static("public")) call the middleware for every request, even if it wasn't a request for a static resource?
It will only get called if a route hasn't dealt with the request already.
Keeping in mind that routes are tested in the order they are registered, take this example:
const express = require('express');
const app = express();
const port = 3000;
app.get('/foo', (req, res) => {
console.log('Foo!');
res.send('Foo!');
});
app.use(function (req, res, next) {
console.log('middleware triggered');
next();
});
app.get('/bar', (req, res) => {
console.log('Bar!');
res.send('Bar!');
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
If I request http://localhost:3000/foo then the server will log:
Foo!
The /foo endpoint matched the request and then called res.send().
If I request http://localhost:3000/bar then it logs:
middleware triggered
Bar!
The middleware kicks in (because it matches the route), called next() to go to the next function that matches the route, and then the /bar handler is called.
It is important to position your static middleware carefully.
If you put it before the route you want to match the request then there are two possible negative effects:
You'll call it when it isn't needed which is inefficient
A static file will match a route instead of an actual route handler
On the other hand, if you put it last then you'll solve the efficiency problem, but some bad route design might mean that something creates a URL which matches an already existing static file and masks it.
It's a good idea to specify a directory that you know will never conflict with a route (e.g. app.use('/static', express.static('public'));) to avoid that possibility. As a bonus it means that any broken links which would normally 404 won't have to go through the static middleware unless the link is pointing in the /static path in the first place.
When registering it like that it, the middleware will run on every request, yes.
Basically because that statement is actually the same as:
app.use("/", express.static("public"))
Calling express.static returns a classic middleware function that will be run on every path you specify in app.use.
If you want it only to kick in on a specific path, you could register it like this:
app.use('/static', express.static('public'));

passing url parameters to other page using express and node js

so i have a chat app that i am working on here and i am trying to make it to where a user can type in like https://niknet.ddns.net/room/randomroomUID and it will join them to that room if they are logged in (i use password for handling login and signup) my code looks like this on my routes.js (in case you dont know it's the js that handles all my express stuff)
i tried this
app.get('/room/:roomUid', isLoggedIn, function(req, res) {
console.log(req.params.roomUid)
console.log(req.user)
res.cookie('uid',req.user.uid, { maxAge: 999999999999999999999});
var username = req.user.displayName;
app.post( 'apolloMainchat',{ roomUid:req.params.roomUid , username});
});
but im not sure how to pass the information across to the express code that handles when users /apolloMainchat
typically a user can get to /apolloMainchat like this
app.get('/apolloMainchat', isLoggedIn, function(req, res) {
console.log(req.user)
res.cookie('uid',req.user.uid, { maxAge: 999999999999999999999});
var username = req.user.displayName;
res.render('apolloMainchat.ejs', username);
console.log(req.user.displayName)
});
any one know how to help me?
You will want to pass this by redirecting the client to a new endpoint and then reading those parameters from the query as Renato Gama said in the comments.
Your code will then become something like this.
app.get('/room/:roomUid', isLoggedIn, function(req, res) {
res.cookie('uid',req.user.uid, { maxAge: 999999999999999999999});
app.redirect('apolloMainchat?roomUid=' + req.params.roomUid);
});
app.get('/apolloMainchat', function(req, res, next) {
console.log(req.params.roomUid);
});
You're just using a HTTP redirect to pass them to the new endpoint and attaching the data as a query parameter as you would if they requested it directly. It's not quite clear what you're trying to achieve, but this should do what you've described.

Proper way of setting up access rights?

I am creating a user management system - However I am current finding myself checking the user type on a per router bases.
router.get('/admin/settings', (req, res) => {
if(admin) {
//Proceed.
}
}
router.get('/admin/users', (req, res) => {
if(admin) {
//Proceed.
}
}
Is there a better way of doing this? Can't I just set a route like this?
router.get('/admin*', (req, res) => {
if(!admin) {
res.status(404).send('Not found')
}
}
(I have tried and not succeeded, feels like it clashes with other routes)
Also, on a similar note. How Am I supposed to handle denying a user access to a script? Do I send a 404 or 403?
You can use an Express middleware function:
router.use(function(req, res, next) {
if(admin) {
return next();
}
// We fail closed
return res.status(403).send('Forbidden');
});
// This won't get called if the middleware doesn't call next()
router.get('/admin/settings', (req, res) => {
// Do stuff
}
Here, we call next() only if the user is an admin, which allows the call to continue. Any routes added after this middleware will be protected.
Also, on a similar note. How Am I supposed to handle denying a user access to a script?
A 403 is the appropriate code here, though a 404 can also be used if you wish to hide the route from unauthorized clients. I would suggest reading up on what each code is designed for.

Express.js and Angular.js html5mode (Avoiding # in url)

I have been working on avoiding # in Angular app with ExpressJS serverside.
I have researched how to enable html5mode and it worked great. But whenever there is another 'get' request to retrieve data from another url such as /api/services, it seems like somehow broken and do not provide data properly to the page.
Here's what I have done in express end.
router.get('/*', function (req, res, next) {
res.render('index');
});
router.get('/api/service-edit', function (req, res, next) {
Service.find(function (err, services) {
if (err) {return next(err);}
res.json(services);
});
});
I am not 100% sure, but what I guess is '/*' causes a problem for the server in reading api urls.
I am wondering if any of you have an experience in this problem and a solution to figure this out.
Thank you for your time.
In expressjs the routing rules are matched in the order you present them in the code. So the '/*' rule is always matched first and the second rule is never reached.
Putting the second rule before the first one will probably solve the problem.
Hope it helps.

Is this a good / secure way to set server side cookies from client

I am working with a single app application framework called reactjs, the issue I encountered is setting httpOnly cookies, as they can not be set / read from a client side I needed to figure out a way how to use express for this.
One idea I came up with is to make a post request to a route like /cookie:data where data is value of token that needs to be stored in a cookie, so:
app.post('/cookie:data', function(req, res) {
// Set cookie here
res.send(200)
})
Issue I am hesitant about is that token contains unique user identifier that is used to secure api, and I am not sure if I am or am not exposing this by setting up a cookie this way.
Alternatively instead of using :data it would be beneficial to figure out how I can grab data (json object) from the post request
EDIT:
One issue I can think of is that anyone can post to this route and set different cookies? what would be a way of securing it?
EDIT 2:
This is the express setup I use to proxy api calls (only relevant for clarifying comments)
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})
Say that you want to proxy all requests starting with /api to a third-party, except /api/users, which you want to perform 'manually' because it returns a token you need:
app.post('/api/users', function(req, res) {
let url = config.API_HOST + req.url;
let apiRequest = request.post(url, function(err, response, body) {
// Responses are just examples, you should tailor them to your situation
if (err) {
return res.sendStatus(500);
} else if (response.statusCode !== 200) {
return res.sendStatus(response.statusCode);
} else {
res.cookie('token', body).send('OK');
}
});
req.pipe(apiRequest);
})
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})

Categories

Resources