news 2026/9/20 23:36:17

Sails.js 请求对象 `req.originalUrl` 详解:获取未被重写前的原始请求 URL

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sails.js 请求对象 `req.originalUrl` 详解:获取未被重写前的原始请求 URL

Sails.js 请求对象req.originalUrl详解:获取未被重写前的原始请求 URL

【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails

req.originalUrl是 Sails.js 请求对象(req)上的一个字符串属性,用于保留客户端最初请求的 URL,即使req.url在内部路由、策略(policy)或中间件中被重写,你依然可以拿到原始地址。本文以 Sails.js 仓库中的官方参考文档为主线,结合 请求对象构造源码 与 单元测试 深入讲解该属性的语义、底层实现与典型应用场景,帮助你在开发控制器、策略与自定义中间件时准确判断"用户到底访问了哪个地址"。

属性定义与官方语义

根据 Sails.js 对 Express 4.x API 的引用,req.originalUrl的定义如下:

该属性与req.url非常相似,但它保留了原始请求 URL,允许你在内部路由时自由重写req.url

用一句话概括:req.originalUrlreq.url的"不可变快照",它只反映客户端最初发起的请求地址;而在一次请求的完整生命周期中,req.url则可能被框架内部或开发者自己的代码改写。

Sails.js 在绝大多数情况下都建议优先使用req.url。只有当req.url被修改过(例如在某个策略或中间件里为了把请求转到内部路由而重写它)时,req.originalUrl才会体现出不可替代的价值——它始终给你那个"最初被请求的 URL"。

基本用法

req.originalUrl是一个只读字符串属性,直接访问即可,无需任何参数:

req.originalUrl; // => "/search"

req.url相同,它包含路径和查询字符串(query string)后缀,但不包含 URL 片段(fragment / hash,即#之后的部分)。例如:

// 客户端请求: GET /search?q=worlds%20largest%20dogs req.originalUrl; // => "/search?q=worlds%20largest%20dogs" req.url; // => "/search?q=worlds%20largest%20dogs"

在未发生任何重写的情况下,req.originalUrlreq.url的值完全一致;二者的差异只会出现在req.url被修改之后。

源码实现:req.originalUrl从哪来

在 Sails.js 中,通用的请求对象由 lib/router/req.js 中的buildRequest(_req)工厂函数构造。该函数是 Sails 实现传输无关(transport-agnostic)中间件支持的基础,既服务于 socket.io 等 hook,也服务于应用级与内核级测试。

构造过程中,originalUrl的赋值逻辑位于 lib/router/req.js#L150:

req = defaultsDeep(req, { params: [], query: (_req && _req.query) || require('querystring').parse(parsedUrl.query) || {}, body: (_req && _req.body) || {}, param: function(paramName, defaultValue) { /* ... */ }, wantsJSON: (_req && _req.wantsJSON === false) ? false : true, method: 'GET', originalUrl: _req.originalUrl || _req.url, path: _req.path || parsedUrl.pathname }, _req||{});

从源码结构可以看出两个关键点:

  1. 回退链originalUrl优先取传入的_req.originalUrl,若上层(例如真实的 HTTP 服务器)没有提供该字段,则回退为_req.url。也就是说,即使底层请求对象根本没有originalUrl概念,Sails 也能保证req.originalUrl始终是一个可用的字符串。
  2. req.url同源:当请求进入 Sails 时,req.url_req.url携带(见 lib/router/req.js#L76 构造 MockReq 时的url字段),而originalUrl正是以它作为初始快照。二者在请求生命周期的起点指向同一个地址。

此外,构造请求时 Sails 还会用parseurl解析原始 URL,以便从中分离出路径与查询字符串(lib/router/req.js#L38-L40),这与req.pathreq.query的取值同源。

测试验证:单元测试如何锁定行为

Sails 仓库在 test/unit/req.test.js 中对req.originalUrl的行为有明确的断言。测试用buildReq({url: '/hello?abc=123&foo=bar'})构造一个虚拟请求,然后校验各属性:

it('.originalUrl', function() { req.originalUrl.should.be.an.String; req.originalUrl.should.equal('/hello?abc=123&foo=bar'); });

与之并列的断言还验证了(test/unit/req.test.js#L87-L95):

  • req.path/hello(仅路径,不含查询字符串);
  • req.url/hello?abc=123&foo=bar(路径 + 查询字符串);
  • req.query被解析为{ abc: '123', foo: 'bar' }

这组测试从侧面印证了一个事实:在未重写的情况下,req.originalUrlreq.url与"路径 + 查询串"三者保持严格一致;而req.path则是剥离查询串后的纯路径。

典型应用场景:当req.url被重写时

Sails 内部的路由与中间件机制中,req.url确实存在被改写的可能性。例如在 lib/router/index.js 的路由处理流程中,框架会基于req.url提取并合并查询参数(lib/router/index.js#L508-L510):

var queryStringPos = req.url.indexOf('?'); if (queryStringPos !== -1) { req.query = _.merge(req.query, QS.parse(req.url.substr(queryStringPos + 1))); }

同时,lib/router/bind.js 中的skipRegexesWrapper也会用req.url.match(regexes[i])判断 URL 是否命中某些正则(lib/router/bind.js#L445-L449),用以跳过特定路由的处理器。这类对req.url的读取与潜在改写,正是req.originalUrl存在的意义。

常见的真实场景包括:

场景一:策略(policy)中的内部重定向

在某个策略里将请求改写后转发给内部路由:

// api/policies/redirect-legacy.js module.exports = function (req, res, next) { // 将旧路径重写为新的内部路由 req.url = '/search?q=' + req.param('q'); return next(); };

此时在后续的 action 中,req.url已经是/search?...,但req.originalUrl仍保留用户最初请求的旧地址。若需要在日志、统计或鉴权逻辑中判断"用户最初访问了哪里",就必须读取req.originalUrl

场景二:自定义中间件中的请求改写

与策略类似,在config/http.js中注册的自定义中间件也可能修改req.url以匹配内部路由。凡是你希望"重写后仍然知道原始地址"的地方,都应该访问req.originalUrl

场景三:日志与审计

在记录请求日志时,同时输出两个字段可以完整还原请求的来龙去脉:

sails.log.info('Original URL: %s | Current URL: %s', req.originalUrl, req.url);

注意事项

  1. 默认情况下请优先使用req.url:正如官方文档反复强调的,绝大多数业务代码并不关心"原始 URL",req.url才是当前路由实际使用的地址。
  2. 查询字符串包含在内req.originalUrlreq.url一样包含?之后的查询字符串,与仅含路径的req.path不同。需要纯路径时,请使用req.path(对应源码中path: _req.path || parsedUrl.pathname的逻辑,见 lib/router/req.js#L151)。
  3. URL 片段(hash)不可用#之后的部分由浏览器端处理,不会随 HTTP 请求发送到服务器,因此req.originalUrlreq.url都取不到它;如果你用res.redirect()返回 302 响应,用户代理会在跳转后的地址上自动保留并追加原 URL 片段。
  4. 始终是字符串:单元测试中明确断言了req.originalUrl.should.be.an.String(test/unit/req.test.js#L98),即便底层请求对象未提供该字段,也会通过回退_req.url保证其类型与可用性。

相关文档与参考

  • req.url:与req.originalUrl同源但可被重写的当前请求地址
  • 请求对象构造源码:originalUrl的回退赋值逻辑(第 150 行)
  • 请求对象单元测试:对req.originalUrl等属性的行为断言(第 97-100 行)
  • Sails 路由器核心实现:基于req.url的查询参数解析流程
  • 路由绑定源码:正则路由包装器对req.url的匹配逻辑

【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 23:34:43

TiXL 节点详解:Combine3Images —— 用三张输入图像重组 RGBA 通道

音视频图形学桌面应用 【免费下载链接】t3 TiXL is an open source software to create realtime motion graphics. 项目地址: https://gitcode.com/GitHub_Trending/t3/t3 点击查看 免费下载 本篇技术指南围绕 TiXL(tooll3/t3 实时动态图形创作工具&am…

作者头像 李华
网站建设 2026/9/20 23:33:57

DevSecOps标准解读:从安全左移到流水线门禁落地实践

简介:DevSecOps标准解读PDF是一份面向网络安全、研发运维与合规管理从业者的标准科普资料,重点解决团队在落地DevOps时安全介入过晚的痛点。资源以安全左移为主线,系统梳理DevSecOps的起源、核心理念,以及计划、开发、构建、测试、…

作者头像 李华