如何在Express中获取完整的URL?


486

假设我的示例网址是

http://example.com/one/two

我说我有以下路线

app.get('/one/two', function (req, res) {
    var url = req.url;
}

url将是/one/two

如何在Express中获取完整的URL?例如,在上述情况下,我想收到http://example.com/one/two


6
仅供参考,您可以检查请求对象并进行浏览,但是我是伪君子,在这里找到了它。
Jason Sebring

Answers:


742
  1. 该协议可作为req.protocol这里的文档

    1. 在express 3.0之前,http除非您看到req.get('X-Forwarded-Protocol')已设置并具有value 的协议,否则可以假定为该协议https,在这种情况下,您知道这是您的协议
  2. 主机来自req.get('host')Gopal指示

  3. 希望您在URL中不需要非标准端口,但是如果您确实需要知道它,则可以将其置于应用程序状态,因为这是app.listen在服务器启动时传递给您的。但是,在使用非标准端口进行本地开发的情况下,Chrome似乎将端口包含在主机标头中,因此例如req.get('host')返回localhost:3000。因此,至少对于在标准端口上的生产站点并直接浏览到快速应用程序(没有反向代理)的情况,host标头似乎对URL中的端口做了正确的处理。

  4. 路径来自req.originalUrl(感谢@pgrassant)。请注意,此DOES包含查询字符串。此处有关req.url和req.originalUrl的文档。根据您打算对URL originalUrl进行的操作,与相比,该值可能是正确的值,也可能不是正确的值req.url

将所有内容组合在一起以重建绝对URL。

  var fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;

3
@dave客户端可以发送它想要的任何标头(以及任何URL,端口,随机的非HTTP垃圾),但是,在某些时候,虚假或不正确的标头只会导致协议失败。例如,在虚拟主机环境中,错误的“主机”标题将显示完全不同的网站。对于X-Forwarded-Protocol,通常不是由实际的客户端(浏览器)发送,而是由反向代理(nginx / varnish / apache /其他)发送,该反向代理在您的应用程序前面提供HTTPS服务。
彼得·里昂斯

59
或者使用req.get('Host')代替req.host主机和端口部分。
diosney 2014年

1
最好为此单独发布一个问题。这个问题是关于快递的。
彼得·里昂斯

18
host可以欺骗请求标头中的参数。如果使用res.host这种方式,则可能会发生“主机头攻击” 。在Django框架中,它们具有一个“允许的主机”变量,用于防止此类攻击。我使用一个配置变量,root_url可以将其添加到中req.url以完成操作。关于攻击:skeletonscribe.net/2013/05/...
阿米尔ELDOR

1
如果要添加req.originalUrl不带参数的对象,只需执行req.originalUrl.split("?").shift()。资料来源:stackoverflow.com/a/32118818/2037431
Marcos Pereira

131

您可以将node.js API用作URL,URL.format()从express 传递信息,而不是将它们自己串联在一起。

例:

var url = require('url');

function fullUrl(req) {
  return url.format({
    protocol: req.protocol,
    host: req.get('host'),
    pathname: req.originalUrl
  });
}

4
在我的情况下,req.get('host')仅返回主机名部分,而不返回端口。不知道为什么,但是现在我从设置中收集端口号,并使用hostname字段代替host
maxkoryukov

1
相反pathname,我认为你的意思是path。其中包括搜索/查询字符串
费利克斯·桑斯

3
这不适用于具有查询字符串的URL。
基思

37

我发现获取请求的网址有点像PITA。我不敢相信没有比这更简单的表达方式了。应该只是req.requested_url

但是,这是我的设置方式:

var port = req.app.settings.port || cfg.port;
res.locals.requested_url = req.protocol + '://' + req.host  + ( port == 80 || port == 443 ? '' : ':'+port ) + req.path;

必须定义端口变量?
Amol M Kulkarni

4
是否req.port存在?它不在Express文档中吗?
Mitar

我的错。我以为您会知道要使用哪个端口并预先设置该端口。我将再次更新示例。您也可以通过req.app.settings.port
chovy

当req.protocol为空时,是否表示http?
码拍

这不包括查询。还不完整。可接受的答案更好。
Kostanos '18年

17

使用url.format

var url = require('url');

这支持所有协议并包括端口号。如果您的originalUrl中没有查询字符串,则可以使用以下更清洁的解决方案:

var requrl = url.format({
    protocol: req.protocol,
    host: req.get('host'),
    pathname: req.originalUrl,
});

如果您有查询字符串:

var urlobj = url.parse(req.originalUrl);
urlobj.protocol = req.protocol;
urlobj.host = req.get('host');
var requrl = url.format(urlobj);

16

这是添加可以在req对象上调用以获取url的函数的好方法

  app.use(function(req, res, next) {
    req.getUrl = function() {
      return req.protocol + "://" + req.get('host') + req.originalUrl;
    }
    return next();
  });

现在,您有了一个可以按需调用的功能。


2
这不包括用户名:密码,你可以在一个完整的URL获得“ 用户:pass@host.com:8080 / P / A / T / H查询字符串=#散
代码唯一

@CodeUniquely是正确的,但是由于该约定已被弃用十多年了,因此希望没人能在其API中真正构建用户信息规范
Mike

11

Express在代理之后时,使req.host/req.hostname有效必须具有两个条件:

  1. app.set('trust proxy', 'loopback'); 在app.js中
  2. X-Forwarded-Host标头必须由您自己在网络服务器中指定。例如。apache,nginx

nginx

server {
    listen myhost:80;
    server_name  myhost;
    location / {
        root /path/to/myapp/public;
        proxy_set_header X-Forwarded-Host $host:$server_port;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://myapp:8080;
    }
}

阿帕奇

<VirtualHost myhost:80>
    ServerName myhost
    DocumentRoot /path/to/myapp/public
    ProxyPass / http://myapp:8080/
    ProxyPassReverse / http://myapp:8080/
</VirtualHost>


1
是的,没有人告知,如果你在Apache或nginx的不正确的配置,你会得到127.0.0.1req.get('host')
Spark.Bao


7
var full_address = req.protocol + "://" + req.headers.host + req.originalUrl;

要么

var full_address = req.protocol + "://" + req.headers.host + req.baseUrl;

6

您需要使用构建它req.headers.host + req.url。当然,如果您在其他端口托管,那么您就可以了;-)


1
除了协议外,这让我得到了一切……有什么可以告诉我的吗?
克里斯·艾布拉姆斯

为了获得该协议使用:req.protocol
mrded

6

我建议使用originalUrl代替URL:

var url = req.protocol + '://' + req.get('host') + req.originalUrl;

请参阅此处的originalUrl说明: http //expressjs.com/api.html#req.originalUrl

在我们的系统中,我们这样做,所以originalUrl对我们很重要:

  foo = express();
  express().use('/foo', foo);
  foo.use(require('/foo/blah_controller'));

blah_controller看起来像这样:

  controller = express();
  module.exports = controller;
  controller.get('/bar/:barparam', function(req, res) { /* handler code */ });

因此,我们的网址具有以下格式:

www.example.com/foo/bar/:barparam

因此,我们需要在bar控制器get处理程序中使用req.originalUrl。


6

我的代码看起来像这样,

params['host_url'] = req.protocol + '://' + req.headers.host + req.url;


5

我使用节点包“ url”(npm install url)

它的作用是当您打电话时

url.parse(req.url, true, true)

它将使您能够检索全部或部分网址。更多信息在这里: https //github.com/defunctzombie/node-url

我以以下方式使用它来获取http://www.example.com/中 /之后的所有内容,以用作变量并提取特定的配置文件(有点像facebook:http://www.facebook。 com / username

    var url = require('url');
    var urlParts = url.parse(req.url, true, true);
    var pathname = urlParts.pathname;
    var username = pathname.slice(1);

尽管要执行此操作,但必须在server.js文件中以这种方式创建路由:

self.routes['/:username'] = require('./routes/users');

并以此方式设置您的路由文件:

router.get('/:username', function(req, res) {
 //here comes the url parsing code
}

2

您可以在这样的路线中使用此功能

app.get('/one/two', function (req, res) {
    const url = getFullUrl(req);
}

/**
 * Gets the self full URL from the request
 * 
 * @param {object} req Request
 * @returns {string} URL
 */
const getFullUrl = (req) => `${req.protocol}://${req.headers.host}${req.originalUrl}`;

req.protocol将为您提供http或https, req.headers.host将为您提供完整的主机名,例如www.google.com, req.originalUrl将为您提供其余名称pathName(以您为例/one/two


您能否详细说明该解决方案的工作原理。避免在堆栈溢出中仅发布代码答案。
Arcath

@Arcath详细说明。
Awais Ayub

1

谢谢大家提供的信息。这真令人讨厌。

将其添加到您的代码中,您无需再考虑它:

var app = express();

app.all("*", function (req, res, next) {  // runs on ALL requests
    req.fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
        next()
})

您也可以在那里执行或设置其他操作,例如登录到控制台。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.