如何使用Express的req对象获取请求路径

242

我正在使用express + node.js,有一个req对象,在浏览器中发出的请求是/account,但当我记录req.path时,我得到的是'/'而不是'/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是/,但应该是/account吗?


4
类型错误:无法读取未定义的属性“path”。 - chovy
req.route.path是正确的,并且在这里有文档记录(http://expressjs.com/api.html#req.route)。您使用的是哪个版本的Express? - zemirco
我遇到了同样的问题。req.route未定义。我正在使用express 3.4.4。什么原因会导致路由未定义? - davidpfahler
@vinayr req.route.path仍然给出/create而不是/quizzes/create,这是整个URL。 - Sandip Subedi
它只在中间件处理程序内未定义。在路由处理程序内,肯定有一条路线。 - Omar Dulaimi
显示剩余3条评论
11个回答

333

在我自己试玩了一下之后,你应该使用:

console.log(req.originalUrl)


4
我认为这与中间件的定位有关,但你是正确的,这没有意义。 - Menztrual
1
这绝对是中间件的问题。在Express 4中使用其他路由器时也会出现这种情况。当它们被挂载到给定路径之外时,在内部它们可以假装自己是根目录下的一部分。这对于隔离来说很好,但是当你不知道如何获取原始完整值时就会很棘手。感谢您发布这个问题! - juanpaco
3
针对4.0版本的Express框架,req.url属性被设计成可通过中间件进行修改以实现重定向,而req.path属性在调用位置不同可能会缺少挂载点。参考链接:http://expressjs.com/en/api.html#req.originalUrl - Christian Davis
3
如果你不想要查询字符串被包含在内:const path = req.originalUrl.replace(/\?.*$/, ''); - Adam Reis
4
警告:这是一个误导性的答案,基于问题的原始提问。如果查询字符串存在,它也将返回查询字符串(例如? a = b&c = 5)。请参见http://expressjs.com/en/api.html#req.originalUrl - Ciabaros
显示剩余2条评论

100

这里有一个从文档中扩展出来的例子,它很好地总结了你在使用express时需要了解的有关路径/URL访问的所有情况:

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()
})

基于:https://expressjs.com/en/api.html#req.originalUrl

结论:c1moore的回答所述,使用:

var fullPath = req.baseUrl + req.path;

83
在某些情况下,你应该使用:
req.path

这将提供给你路径,而不是完整的请求URL。例如,如果你只对用户请求的页面感兴趣,而不是所有种类的URL参数:

/myurl.htm?allkinds&ofparameters=true

req.path会返回:

/myurl.html

1
请注意,如果您正在对此URL进行检查,请确保在应用程序中有效时还包括尾随斜杠的检查(即检查“/demo”也可能要检查“/demo/”)。 - Vinay
如果您不想包含查询字符串:const path = req.originalUrl.replace(/?.*$/, ''); - Adam Reis
req.pathurl.parse(req.url).pathname 的简写,这应该是被接受的答案。 - sertsedat
3
不正确。req.path给出了应用程序挂载的相对路径。如果它被挂载在根目录下,那么这是正确的,但是针对路径为 /my/path 的应用程序,在挂载点为 /my 时,req.url会给出 /path - Stijn de Witt

17

当在基本模块中直接调用时,这可能会产生不同的结果,例如主文件(如index.jsapp.js)与通过app.use()中间件从模块内部调用时(如路由文件routes/users.js)。

API调用:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en

我们将与上述API调用的输出进行比较。

首先,我们将看到来自内部模块的结果:

  1. 我们将把用户模块放在路由目录中,其中包含一个路由,即/profile/:id/:details

    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 without port
        console.log(req.headers.host);    // hostname with port number (if any); same result with req.header('host')
        console.log(req.route.path);      // exact defined route
        console.log(req.baseUrl);         // base path or group prefix
        console.log(req.path);            // relative path except query params
        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;
    
  2. 现在,我们将在应用程序的主模块中导入此用户模块,并使用app.use()中间件将/api/users添加为用户模块的基本路径

    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'));
    

输出

[req.protocol] ............. http
[req.hostname] .......... localhost
[req.headers.host] ..... localhost:8000
[req.route.path] .......... /profile/:id/:details
[req.baseUrl] .............. /api/users
[req.path] ................... /profile/123/summary
[req.url] ...................... /profile/123/summary?view=grid&leng=en
[req.originalUrl] .......... /api/users/profile/123/summary?view=grid&leng=en

完整的URL:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en


现在,如果我们直接在主模块中添加路由,我们将看到结果:

我们将在主模块(即app.js或index.js)中定义路由,并连接基本路径/app/users与路由路径,而不是使用中间件。因此,路由将变为/api/users/profile/:id/:details 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 without port console.log(req.headers.host); // hostname with port number (if any); same result with req.header('host') console.log(req.route.path); // exact defined route console.log(req.baseUrl); // base path or group prefix console.log(req.path); // relative path except query params 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')); ```

输出

[req.protocol] ............. http
[req.hostname] .......... localhost
[req.headers.host] ..... localhost:8000
[req.route.path] .......... /api/users/profile/:id/:details
[req.baseUrl] ..............
[req.path] ................... /api/users/profile/123/summary
[req.url] ...................... /api/users/profile/123/summary?view=grid&leng=en
[req.originalUrl] .......... /api/users/profile/123/summary?view=grid&leng=en

完整的URL:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en

我们可以清楚地看到上面的输出中唯一的区别是baseUrl为空字符串。因此,originalUrl也会改变,并且与url相同。


3
req.route is undefined - Jovanni G
你能分享一下你的代码片段吗?(例如JSFiddle等) - TalESid
示例代码是正确的,但答案中的结果不正确。在第二个示例中,“/api/users”根本没有出现在发布的输出中,在那种情况下甚至没有意义。 - Eric Haynes
在第二个示例的输出中,我根本没有列出“/api/users”。请注意,“[baseUrl]”对应的值为空。 - TalESid

16

8年后的更新:

req.path已经在这里做了我之前提到的完全相同的事情。我不记得这个答案是如何解决问题并被接受为正确答案的,但目前它不再是一个有效的答案。请忽略这个答案。感谢@mhodges提到这一点。

原始回答:

如果您只想获取没有查询字符串的"path",可以使用url库来解析并获取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();
    }
});

这正是我想要的。如果登录成功,可以使用req.query.ref - Ryan Wu
这个与通用代码很好地配合,因为它符合位置规范。记住的东西更少,跨客户端和服务器进行单元测试更容易。 - cchamberlain
1
req.path is just an alias for url.parse(req.url).pathname - mhodges
1
@mhodges 哇,看起来完全正确。这是一个8年前的答案,我不知道当时我们怎么想到这个答案可以解决问题。谢谢你提醒。我会更新答案或者直接删除它。 - Murat Çorlu

13
//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是/,但应该是/account?

原因是Express会减去你的处理程序函数所挂载的路径,这种情况下是'/account'

为什么Express要这样做呢?

因为这使得重用处理程序函数变得更加容易。例如,你可以编写一个处理程序函数,在req.path === '/'req.path === '/goodbye'时执行不同的操作:

function sendGreeting(req, res, next) {
  res.send(req.path == '/goodbye' ? 'Farewell!' : 'Hello there!')
}

然后你可以将它挂载到多个端点:

app.use('/world', sendGreeting)
app.use('/aliens', sendGreeting)

给予:

/world           ==>  Hello there!
/world/goodbye   ==>  Farewell!
/aliens          ==>  Hello there!
/aliens/goodbye  ==>  Farewell!

我很惊讶这个答案没有得到更多的投票,因为它清楚地回答了问题并解释了行为(不像所有得到更高投票的答案)。谢谢! - Mark Longair

12

应该是:

req.url

Express 3.1.x


1
req.url不是Express的本机属性,而是从Node的http模块继承而来的: https://expressjs.com/en/api.html#req.originalUrl - Megagator

11

在版本4.x中,您现在可以使用req.baseUrl以获取完整路径,除了req.path。例如,操作员现在可以执行以下操作:

//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();
  }
});

6

req.route.path对我来说可行

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);
        });
    });
};

执行后,我在控制台看到以下内容,并且在浏览器中获得了完美的结果。

Express server listening on port 3000 in development mode
A call to route: /plants
Request type: get

1

在使用express中间件时,您的请求对象具有多个属性,可用于获取正确的路径:

  • req.baseUrl:/api/account
  • req.originalUrl:/api/account
  • req._parsedUrl.path:/account
  • req._parsedUrl.pathname:/account
  • req._parsedUrl.href:/account
  • req._parsedUrl._raw:/account

请注意:此适用于中间件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接