Sails.jsreq.query属性完全指南查询字符串解析原理与实战用法【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sailsreq.query是 Sails.js 请求对象Request上的一个核心属性它以字典对象形式保存经过解析的 URL 查询字符串query-string默认值为空对象{}。无论你是在控制器Controller、策略Policy还是自定义中间件中读取 GET 请求的过滤、分页或搜索参数都会与它打交道。读完本文你将掌握req.query的准确定义、解析时机与底层实现并能结合req.param()、req.allParams()正确地在实战中读取各类请求参数。req.query是什么按官方参考文档 req.query.md 的定义一个包含解析后的查询字符串的字典dictionary默认值为{}。也就是说只要请求 URL 的?之后带有查询参数如?qmudslidepage2Sails 就会把它们解析为一个普通的 JavaScript 对象并通过req.query暴露给应用代码。如果请求没有携带任何查询字符串req.query依然是一个合法的对象{}因此你可以放心地直接对它做属性访问或遍历而不会碰到undefined报错。基本用法req.query;属性类型Object普通字典默认值{}适用场景读取 URL 查询字符串中的参数官方示例如果请求是GET /search?qmudslidereq.query.q // - mudslidereq.query中的每个键值对都直接来自查询字符串键为参数名值为参数字符串。上例中req.query的实际内容等价于{ q: mudslide }。req.query是怎么被填充的源码级解析理解req.query的关键在于搞清楚它是在什么时机、由哪段代码解析出来的。在当前仓库中存在两条主要的请求路径它们都负责维护req.query。1. 通用req构建器lib/router/req.jsSails 的传输无关请求对象由 lib/router/req.js 中的buildRequest工厂函数创建它构成了 Sails 对 Connect/Express 中间件的通用支持基础既被 socket 等 hooks 使用也被测试与 Sails 核心复用。在构建req时该文件会尝试解析 URL 以取得查询字符串并把结果作为query的默认值写入请求对象lib/router/req.js#L120-L124req defaultsDeep(req, { params: [], query: (_req _req.query) || require(querystring).parse(parsedUrl.query) || {}, body: (_req _req.body) || {}, // ... }, _req||{});这段代码揭示了三点事实如果上游已经提供了_req.query例如来自 Express则直接沿用否则使用 Node 内置模块querystring的parse()方法解析parsedUrl.query即 URL 中?之后的原始字符串解析失败或没有查询字符串时回退为空对象{}与官方文档默认值为{}的描述完全一致。2. 虚拟请求路径lib/router/index.js中的qsParser对于通过sails.request()发起的虚拟请求包括 socket.io 请求的底层处理Sails 在 lib/router/index.js 中内置了一个极其简单的查询字符串解析器qsParserlib/router/index.js#L506-L516function qsParser(req,res,next) { var queryStringPos req.url.indexOf(?); if (queryStringPos ! -1) { req.query _.merge(req.query, QS.parse(req.url.substr(queryStringPos 1))); } else { req.query req.query || {}; } next(); }它在路由处理之前作为基础中间件运行lib/router/index.js#L207-L212定位 URL 中第一个?截取其后的查询字符串交给 Node 内置querystring模块解析再与已有的req.query合并。整个流程用sails.log.silly(Handling virtual request :: Running virtual querystring parser...)记录了调试日志便于追踪。3. HTTP 请求路径对于标准的 HTTP 请求Sails 建立在 Express 之上req.query由 Express 层的查询解析中间件填充随后 Sails 的请求对象同样会持有该属性。在 lib/app/request.js 的sails.request()实现中也有一段对查询字符串的处理逻辑lib/app/request.js#L85-L99其要点是当通过sails.request()以GET、HEAD或DELETE方法发起请求且传入了对象形式的body时Sails 会把它序列化为查询字符串并拼接到 URL 上——这点我们会在后文虚拟请求中的行为展开。req.query与req.params、req.body的边界在实际开发中一个请求可能同时携带三种不同的参数正确区分它们是避免 bug 的前提属性参数来源典型示例解析结果类型req.params路由路径中的动态段GET /user/:id中的:id字符串由路由匹配填充req.queryURL 中?之后的查询字符串GET /user?id7中的id字符串req.body请求体表单、JSON 等POST /user提交的字段依解析器而定举个例子对于请求GET /user/7?tabposts并匹配路由get /user/:idreq.params.id // - 7来自路径 req.query.tab // - posts来自查询字符串 req.query.id // - undefined注意req.query中的值始终是字符串或由查询解析器产生的数组等结构即使你传的是数字?page2读取到的也是字符串2需要时请自行用Number()转换。统一读取参数的便捷方法req.param()与req.allParams()虽然req.query是读取查询参数的直接途径但 Sails 还提供了两个更高层的方法让你不必在req.params、req.query、req.body之间手动来回切换。req.param(param, defaultValue)的查找顺序在 lib/hooks/request/param.js 中Sails 实现了req.param()作为 Express 4 移除req.param()之后的门面facade方法其查找顺序是路由参数req.params[param]请求体req.body[param]查询字符串req.query[param]若以上都没有返回defaultValue// lib/hooks/request/param.js核心逻辑 req.param function(param, defaultValue) { if (typeof req.params[param] ! undefined) { return req.params[param]; } if (req.body typeof req.body[param] ! undefined) { return req.body[param]; } return typeof req.query[param] ! undefined ? req.query[param] : defaultValue; };该 mixin 通过sails.on(router:route, ...)事件在每条路由匹配前注入lib/hooks/request/index.js#L52-L67注释明确说明必须在每个路由上应用而不是每个请求因为req.params会随匹配到的路由而变化。这也解释了为什么req.query是开发中最常直接访问的属性——它不依赖路由匹配结果。req.allParams()合并三种来源lib/hooks/request/params.all.js 实现了req.allParams()它把查询字符串、请求体和路由参数合并为一个对象var allParams _.extend({}, req.query, req.body); // 再将 req.params 中已定义的路由参数并入合并优先级为req.body覆盖req.query的同名键路由参数再覆盖前两者。该文件还保留了一段被删除的历史代码注释记录了 Sails v1.0 的一个重要变更req.params.all()已在 v1.0 中移除请改用req.allParams()。这一变更据注释所述是出于性能考虑Object.defineProperty()较慢。虚拟请求sails.request()中的req.query行为Sails 允许在服务器端代码中直接发起虚拟请求virtual request例如在测试中模拟 HTTP 调用。在 lib/app/request.js 的sails.request()实现中有一个与req.query直接相关的设计lib/app/request.js#L85-L99如果这是GET、HEAD或DELETE请求则将传入的body视为参数序列化进查询字符串。// 如果 body 是对象且方法是 GET/HEAD/DELETE var stringifiedParams QS.stringify(body); if (queryStringPos -1) { url ? stringifiedParams; } else { url url.substring(0, queryStringPos) ? stringifiedParams; }这意味着当你用sails.request({ method: GET, url: /search }, { q: mudslide })发起虚拟请求时{ q: mudslide }会被拼接到 URL 上成为?qmudslide最终同样通过req.query.q被控制器读到。这一机制让以 GET 语义传参数在虚拟请求环境中也能正常工作。Blueprints 如何消费req.querySails 的 Blueprint API自动 REST 路由也是req.query的重度消费者。在 lib/hooks/blueprints/parse-blueprint-options.js 中可以看到 Blueprint 会从req.query读取关联association相关的查询参数lib/hooks/blueprints/parse-blueprint-options.js#L268-L270if (attrDef.collection (!req.body || !req.body[attrName]) (req.query _.isString(req.query[attrName]))) { values[attrName] JSON.parse(req.query[attrName]); }也就是说当某个属性在模型上被定义为collection一对多/多对多关联时Blueprint 会优先从req.query中读取该属性名并尝试将其作为 JSON 字符串解析——这是populate相关查询如GET /pet?owner1在底层读取查询参数的方式之一。此外关联 ID 列表也会从req.query[req.options.alias]读取同文件 L410。这提醒我们查询字符串并非只能用于简单的键值过滤Sails 的框架级功能也会按约定从req.query取值。常见实战用法结合req.query的特性以下是最典型的控制器内用法。搜索与过滤// GET /search?qmudslide module.exports { search: async function (req, res) { const q req.query.q || ; const results await Article.find({ title: { contains: q } }); return res.ok(results); } };分页与排序// GET /articles?page2limit10sortcreatedAt module.exports { list: async function (req, res) { const page Number(req.query.page) || 1; const limit Number(req.query.limit) || 10; const sort req.query.sort || id; const articles await Article.find() .sort(sort) .skip((page - 1) * limit) .limit(limit); return res.ok(articles); } };在策略Policy中读取权限参数// 例如某策略判断是否携带了合法的来源标记 module.exports function (req, res, next) { if (req.query.from req.query.from internal) { return next(); } return res.forbidden(); };测试如何验证req.query仓库的单元测试直接印证了本文描述的行为。在 test/unit/req.test.js 中无查询字符串的请求/断言req.query是对象且为空L41-L44对应默认值为{}带查询字符串的请求/hello?abc123foobar断言req.query正确包含abc: 123与foo: barL76-L80同时验证req.param(abc)与req.param(foo)也能取到相同值L82-L85。这些测试用例可以作为你验证自定义解析行为或排查问题的参照模板。注意事项与易踩的坑值永远是字符串req.query.page得到的是2而非2做算术运算前务必转换类型。重复参数?tagatagb的解析结果取决于底层querystring.parse的行为通常得到数组跨环境HTTP 中间件 vs 虚拟请求时行为可能不同建议避免依赖重复参数。不要与req.params混淆路径参数永远优先于查询参数被 Sails 的路由机制处理二者命名空间独立。敏感数据不要放查询字符串req.query内容会出现在日志、URL 历史与 Referrer 中密码、令牌等请放入请求体或请求头。req.params.all()已废弃如果你在旧代码或旧资料中看到req.params.all()在 Sails v1.0 中请改为req.allParams()。相关参考req.query 官方参考req.allParams 参考req.param 参考req.body 参考req.params 参考请求对象构建器源码虚拟请求查询解析器req.param 门面实现req.allParams 实现req.query 单元测试【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考