how to get request path with express req object - javascript

I'm using express + node.js and I have a req object, the request in the browser is /account but when I log req.path I get '/' --- not '/account'.
//auth required or redirect
app.use('/account', function(req, res, next) {
console.log(req.path);
if ( !req.session.user ) {
res.redirect('/login?ref='+req.path);
} else {
next();
}
});
req.path is / when it should be /account ??

After having a bit of a play myself, you should use:
console.log(req.originalUrl)

Here is an example expanded from the documentation, which nicely wraps all you need to know about accessing the paths/URLs in all cases with express:
app.use('/admin', function (req, res, next) { // GET 'http://www.example.com/admin/new?a=b'
console.dir(req.originalUrl) // '/admin/new?a=b' (WARNING: beware query string)
console.dir(req.baseUrl) // '/admin'
console.dir(req.path) // '/new'
console.dir(req.baseUrl + req.path) // '/admin/new' (full path without query string)
next()
})
Based on: https://expressjs.com/en/api.html#req.originalUrl
Conclusion: As c1moore's answer states, use:
var fullPath = req.baseUrl + req.path;

In some cases you should use:
req.path
This gives you the path, instead of the complete requested URL. For example, if you are only interested in which page the user requested and not all kinds of parameters the url:
/myurl.htm?allkinds&ofparameters=true
req.path will give you:
/myurl.html

UPDATE 8 YEARS LATER:
req.path was already doing exactly same thing that I mentioned here. I don't remember how this answer solved issue and accepted as a correct answer but currently it's not a valid answer. Please ignore this answer. Thanks #mhodges for mentioning this.
Original answer:
If you want to really get only "path" without querystring, you can use url library to parse and get only path part of url.
var url = require('url');
//auth required or redirect
app.use('/account', function(req, res, next) {
var path = url.parse(req.url).pathname;
if ( !req.session.user ) {
res.redirect('/login?ref='+path);
} else {
next();
}
});

This can produce different results when calling directly in base module i.e. main file (e.g. index.js or app.js) vs calling from inside module via app.use() middleware i.e. route file (e.g. routes/users.js).
API call:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en
We'll be comparing our outputs against above API call
1) First, we'll see the result from inside module:
We'll be placing our user module inside routes directory, with one route
routes/users.js
const router = (require('express')).Router();
router.get('/profile/:id/:details', (req, res) => {
console.log(req.protocol); // http or https
console.log(req.hostname); // only hostname (abc.com, localhost, etc)
console.log(req.headers.host); // hostname with port number (if any)
console.log(req.header('host')); // <same as above>
console.log(req.route.path); // exact defined route
console.log(req.baseUrl); // base path or group prefix
console.log(req.path); // relative path except path
console.log(req.url); // relative path with query|search params
console.log(req.originalUrl); // baseURL + url
// Full URL
console.log(`${req.protocol}://${req.header('host')}${req.originalUrl}`);
res.sendStatus(200);
});
module.exports = router;
index.js
const app = (require('express'))();
const users = require('./routes/users');
app.use('/api/users', users);
const server = require('http').createServer(app);
server.listen(8000, () => console.log('server listening'));
Output
http ....................................................................................... [protocol]
localhost .............................................................................. [hostname]
localhost:8000 ..................................................................... [headers.host]
localhost:8000 ..................................................................... [header('host')]
/profile/:id/:details ........................................................ [route.path]
/api/users ............................................................................. [baseUrl]
/profile/123/summary .......................................................... [path]
/profile/123/summary?view=grid&leng=en ........................ [url]
/api/users/profile/123/summary?view=grid&leng=en ..... [originalUrl]
Full URL:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en
2) Now, directly from main module:
We'll define our route right in the starting file (i.e. app.js or index.js)
index.js
const app = (require('express'))();
app.get('/api/users/profile/:id/:details', (req, res) => {
console.log(req.protocol); // http or https
console.log(req.hostname); // only hostname (abc.com, localhost, etc)
console.log(req.headers.host); // hostname with port number (if any)
console.log(req.header('host')); // <same as above>
console.log(req.route.path); // exact defined route
console.log(req.baseUrl); // base path or group prefix
console.log(req.path); // relative path except path
console.log(req.url); // relative path with query|search params
console.log(req.originalUrl); // baseURL + url
// Full URL
console.log(`${req.protocol}://${req.header('host')}${req.originalUrl}`);
res.sendStatus(200);
});
const server = require('http').createServer(app);
server.listen(8000, () => console.log('server listening'));
Output
http ........................................................................ [protocol]
localhost ............................................................... [hostname]
localhost:8000 ...................................................... [headers.host]
localhost:8000 ...................................................... [header('host')]
/profile/:id/:details ......................................... [route.path]
.............................................................................. [baseUrl]
/profile/123/summary ........................................... [path]
/profile/123/summary?view=grid&leng=en ......... [url]
/profile/123/summary?view=grid&leng=en ......... [originalUrl]
Full URL:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en
We can clearly see in above output that the only difference is of baseUrl which is empty string here. So, the originalUrl also changes & looks same as the url

//auth required or redirect
app.use('/account', function(req, res, next) {
console.log(req.path);
if ( !req.session.user ) {
res.redirect('/login?ref='+req.path);
} else {
next();
}
});
req.path is / when it should be /account ??
The reason for this is that Express subtracts the path your handler function is mounted on, which is '/account' in this case.
Why do they do this?
Because it makes it easier to reuse the handler function. You can make a handler function that does different things for req.path === '/' and req.path === '/goodbye' for example:
function sendGreeting(req, res, next) {
res.send(req.path == '/goodbye' ? 'Farewell!' : 'Hello there!')
}
Then you can mount it to multiple endpoints:
app.use('/world', sendGreeting)
app.use('/aliens', sendGreeting)
Giving:
/world ==> Hello there!
/world/goodbye ==> Farewell!
/aliens ==> Hello there!
/aliens/goodbye ==> Farewell!

It should be:
req.url
express 3.1.x

For version 4.x you can now use the req.baseUrl in addition to req.path to get the full path. For example, the OP would now do something like:
//auth required or redirect
app.use('/account', function(req, res, next) {
console.log(req.baseUrl + req.path); // => /account
if(!req.session.user) {
res.redirect('/login?ref=' + encodeURIComponent(req.baseUrl + req.path)); // => /login?ref=%2Faccount
} else {
next();
}
});

req.route.path is working for me
var pool = require('../db');
module.exports.get_plants = function(req, res) {
// to run a query we can acquire a client from the pool,
// run a query on the client, and then return the client to the pool
pool.connect(function(err, client, done) {
if (err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT * FROM plants', function(err, result) {
//call `done()` to release the client back to the pool
done();
if (err) {
return console.error('error running query', err);
}
console.log('A call to route: %s', req.route.path + '\nRequest type: ' + req.method.toLowerCase());
res.json(result);
});
});
};
after executing I see the following in the console and I get perfect result
in my browser.
Express server listening on port 3000 in development mode
A call to route: /plants
Request type: get

For those getting undefined from req.route.path that is correct.
Inside route handler, there's a route.
Inside middleware handlers, there's no route.

When using a middleware in express, your request object has several properties you can use to get the correct path:
req.baseUrl: /api/account
req.originalUrl: /api/account
req._parsedUrl.path: /account
req._parsedUrl.pathname: /account
req._parsedUrl.href: /account
req._parsedUrl._raw: /account
PLEASE NOTE: This applies to middlewares

Related

add json data to middleware request

I am new to express and node, I working on a task where I want to add some json data to middleware request, here is the approach I am following:
In my middleware I want to add some details to request like current date and time and then extract the URL path. Here is the code I came up with:
var express = require('express');
var app = express();
module.exports = app.use('/some-url', function(req, res, next) {
let logdetails = {
id: "some-id",
datetime: Date.now(),
path: '/path-in-url'
}
req.logdetails= logdetails;
next();
} );
I am using module.exports so it this function is exported. But it is giving errors. Also what is the correct way to access the URL path, for example if URL is /users/john then I want to extract /john and my middleware should be applied only when URL starts with /users.
Also what is the correct way to access the URL path, for example if
URL is /users/john then I want to extract /john
If your request url is /users/john then req.path will give you the path of the request url, i.e. /users/john. To extract john, you can use named route parameters and define your route as /users/:name. After using named route parameter in your route, if your request url is /users/john, req.params.name will give you the route parameter, i.e. john. For more details, take a look at req.params on express docs
and my middleware should be applied only when URL starts with /users
following middleware will only run when request path is /users
app.use('/users', (req, res, next) => {
...
}
If you want the middleware then just export the middleware function. Not the Whole app.use part. I think app.use returns an Express object. And :param in your url will make sure that you can access that parameter. You can check out more about path patterns here
middleware.js
module.exports = function(req, res, next) {
let logdetails = {
id: "some-id",
datetime: Date.now(),
path: '/path-in-url'
}
req.logdetails= logdetails;
next();
}
Your other main.js file:
const express = require('express');
const app = express();
const middleWare = require('./middleware')
app.use('/users/:name', middleWare, (req, res)=>{
//you can access logDetails
// and the name
const name = req.params.name;//This will be john for /users/john
const logDetails = req.logdetails;
})
req.originalUrl returns the url path like shown below
// GET /search?q=something
console.dir(req.originalUrl)
// => '/search?q=something'
source: https://expressjs.com/en/api.html
Similarly, get the URL path and split the string accordingly and apply the condition you need.
Here if your originalPath is /users/john then
const result = "/users/john".split("/")
would result in ["", "users", "john"]
Check if the result[1]==="users" and write your condition in the middleware.
You don't have to export the middle ware.
middleware.js
module.exports = {
handler: (req, res, next) => {
const userName = req.params.name;
// add payload to request
next();
}
}
app.js
middleware = require("middleware");
app.use('/users/:name', middleware.handler);
Concerning URL path access you could get access using request params, e.g
app.use('/users/:name', (req, res, next) => {
const userName = req.params.name;
// add payload to request
next();
});
1. Register your specific URL - `/users/:name`, where `:name` is dynamic params e.g *john*
2. Grab params in the callback

Expressjs middleware static typed path and path with param conflict

I'm about to implement REST endpoints for authenticated users and non-authenticated users in expressjs. My current understanding of REST has led me to following design pattern:
User resource
Token user:
/users/self GET, PATCH
Non-token user:
/users POST
/users/:user_id GET
Profile image resource
Token user:
/users/self/profile-images POST
/users/self/profile-images/:profile_image_id PUT, DELETE
Non-token user:
/users/:user_id/profile-images GET
I'm struggling to figure out how to use this pattern without having :user_id parameter become self, i.e {user_id: 'self'}. I would want them to act as two isolated path types without interference, one strict and one dynamic. Is this possible? If so, how?
A code example of my current implementation looks like following:
// instPrivateUserRestRoutes.js (token-user)
router.use('/users/self', [
instAccountRestRoutes(restControllers),
instAuthenticationSessionRestRoutes(restControllers),
instPrivateProfileImageRestRoutes(restControllers)
])
// instPublicUserRestRoutes.js (non-token user)
router.use('/users/:user_id', [
instPublicProfileImageRestRoutes(restControllers)
])
// instRestApp.js (mount point for top resources)
router.use(instPrivateUserRestRoutes(restControllers))
router.use(instPublicUserRestRoutes(restControllers))
What you could do it to create a conditional routing middleware. The factory dunction takes as first argument a callback method that determines which router should be use, and as second argument a list of routers, and returns a new middle war that conditionally uses one of the routes.
function conditionalRouting(cond, routes) {
return function (req, res, next) {
try{
// get the index for the router that should be used
var idx = cond(req, res)
// call this router and pass req, res and next to it
routes[idx](req, res, next)
} catch(err) {
// in the case an error occurs in on of the steps "throw" that error
next(err)
}
}
}
You could then use it that way:
app.use(
'/users/:user_id',
conditionalRouting(
(req, _) => req.params.user_id === 'self' ? 0:1,
[routerForSelf, routerForUser]
))
An other way would be to handle this case explicitly with a middle ware that triggers a not found error:
function forceNotFound(req, res, next) {
var error = new Error('resource not found')
error.status = 404
next(error)
}
And add this as your last middleware
router.use('/users/self', [
instAccountRestRoutes(restControllers),
instAuthenticationSessionRestRoutes(restControllers),
instPrivateProfileImageRestRoutes(restControllers),
forceNotFound
])
This way it is clear that express should stop at that point.
This will look different to what the standard Cannot GET /the/path would look like, but you normally don't want to display those default error messages anyway.
If you want to have the same kind of message then you could write:
var parseUrl = require('parseurl')
var encodeUrl = require('encodeurl')
app.use((req, res, next) => {
var msg = 'Cannot ' + req.method + ' ' + encodeUrl(parseUrl.original(req).pathname)
var error = new Error(msg)
error.status = 404
next(error)
})
If you only want to handled numeric ids you could use this:
router.use('/users/:user_id(\\d+)', [
instPublicProfileImageRestRoutes(restControllers)
])
That way instPublicProfileImageRestRoutes would only be called if user_id is numberic.

How to do whitelist of IP's in Express?

I need to block every IP address from accessing my site except one or two IP's provided by myself. I have tried many modules but nothing seems to work.
var express = require('express')
var AccessControl = require('express-ip-access-control');
var app = express()
app.get('/', function (req, res) {
res.send('Hello World!')
})
var middleware = AccessControl(options);
app.use(AccessControl(options));
var options = {
mode: 'deny',
denys: [],
allows: ['**8.1**.1.**'],
forceConnectionAddress: false,
log: function(clientIp, access) {
console.log(clientIp + (access ? ' accessed.' : ' denied.'));
},
statusCode: 401,
redirectTo: '',
message: 'Unauthorized'
};
app.listen(3000, function () {
console.log(' app listening on port 3000!')
})
on running and accessing my site from my above code i am getting the console message as
::ffff:127.0.0.1 accessed.
::ffff:127.0.0.1 accessed.
::ffff:127.0.0.1 accessed.
::ffff:127.0.0.1 accessed.
any help?
You can simply add your own middleware that checks the IPs, no need to include another module.
You can see the ip from the request with req.connection.remoteAddress.
Before you define your routes, add something like this:
// Custom Middleware
app.use((req, res, next) => {
let validIps = ['::12', '127.0.0.1']; // Put your IP whitelist in this array
if(validIps.includes(req.connection.remoteAddress)){
// IP is ok, so go on
console.log("IP ok");
next();
}
else{
// Invalid ip
console.log("Bad IP: " + req.connection.remoteAddress);
const err = new Error("Bad IP: " + req.connection.remoteAddress);
next(err);
}
})
This will throw an error if an invalid ip comes in. Below all your routes, add something like this:
// Error handler
app.use((err, req, res, next) => {
console.log('Error handler', err);
res.status(err.status || 500);
res.send("Something broke");
});
You need to define your options before you use them. Otherwise, you're passing in undefined to app.use(AccessControl(options)).
Not sure how this is compiling for you, but adding the following line to the top of your script might help show a few more errors that would help.
'use strict';
Secondly, according to the express-ip-access-control documentation:
'allow' mode (Whilelist):
Deny by default, only allow IPs in the whitelist (allows) and not excluded by the blacklist (denys).
So change options.mode from 'deny' to 'allow'.

Calling Express Route internally from inside NodeJS

I have an ExpressJS routing for my API and I want to call it from within NodeJS
var api = require('./routes/api')
app.use('/api', api);
and inside my ./routes/api.js file
var express = require('express');
var router = express.Router();
router.use('/update', require('./update'));
module.exports = router;
so if I want to call /api/update/something/:withParam from my front end its all find, but I need to call this from within another aspect of my NodeJS script without having to redefine the whole function again in 2nd location
I have tried using the HTTP module from inside but I just get a "ECONNREFUSED" error
http.get('/api/update/something/:withParam', function(res) {
console.log("Got response: " + res.statusCode);
res.resume();
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
I understand the idea behind Express is to create routes, but how do I internally call them
The 'usual' or 'correct' way to handle this would be to have the function you want to call broken out by itself, detached from any route definitions. Perhaps in its own module, but not necessarily. Then just call it wherever you need it. Like so:
function updateSomething(thing) {
return myDb.save(thing);
}
// elsewhere:
router.put('/api/update/something/:withParam', function(req, res) {
updateSomething(req.params.withParam)
.then(function() { res.send(200, 'ok'); });
});
// another place:
function someOtherFunction() {
// other code...
updateSomething(...);
// ..
}
This is an easy way to do an internal redirect in Express 4:
The function that magic can do is: app._router.handle()
Testing: We make a request to home "/" and redirect it to otherPath "/other/path"
var app = express()
function otherPath(req, res, next) {
return res.send('ok')
}
function home(req, res, next) {
req.url = '/other/path'
/* Uncomment the next line if you want to change the method */
// req.method = 'POST'
return app._router.handle(req, res, next)
}
app.get('/other/path', otherPath)
app.get('/', home)
I've made a dedicated middleware for this : uest.
Available within req it allows you to req.uest another route (from a given route).
It forwards original cookies to subsequent requests, and keeps req.session in sync across requests, for ex:
app.post('/login', async (req, res, next) => {
const {username, password} = req.body
const {body: session} = await req.uest({
method: 'POST',
url: '/api/sessions',
body: {username, password}
}).catch(next)
console.log(`Welcome back ${session.user.firstname}!`
res.redirect('/profile')
})
It supports Promise, await and error-first callback.
See the README for more details
Separate your app and server files with the app being imported into the server file.
In the place you want to call your app internally, you can import you app as well as 'request' from 'supertest'. Then you can write
request(app).post('/someroute').send({
id: 'ecf8d501-5abe-46a9-984e-e081ac925def',
etc....
});`
This is another way.
const app = require('express')()
const axios = require('axios')
const log = console.log
const PORT = 3000
const URL = 'http://localhost:' + PORT
const apiPath = (path) => URL + path
app.get('/a', (req, res) => {
res.json('yoy')
})
app.get('/b', async (req, res) => {
let a = await axios.get(apiPath('/a'))
res.json(a.data)
})
app.listen(PORT)

NodeJS supertest access to session object

I'm testing my Node.js application with supertest. In my controller I access the session object. In order to make a valid request this session object needs to be filled with some data.
Controller
// determine whether it is user's own profile or not
var ownProfile = userId == req.session.user._id ? true : false;
Test
it('profile', function (done) {
testUserOne.save(function(error, user){
request
.agent(server)
.get('/profile?userId=' + user._id)
.expect('Content-Type', /html/)
.expect(200)
.expect(/Profile/)
.end(done);
})
});
Question
How can I mock the req/session object?
just use as sub-app, and call your authenticated() at parent-app:
var mockApp = express();
mockApp.use(session);
mockApp.all('*', function(req, res, next) {
//authenticated(req, res, next);
//OR
req.session.uid = 'mock uid';
next();
});
mockApp.use(app);
all your routes will be authenticated before matched!
This new lib should do the trick:
https://github.com/rjz/supertest-session
You might also have a look at superagent that is part of supertest. Here's a nice tutorial on this:
http://jaketrent.com/post/authenticated-supertest-tests/
Don't want another lib? Try this one ...
https://gist.github.com/joaoneto/5152248

Categories