Sails Find 蓝图(Blueprint)完全指南:列表查询、过滤、分页、排序与实时订阅
【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails
本篇指南围绕 Sails 内置的Find blueprint 端点展开,讲解如何通过GET /:model查询符合指定条件的记录列表,并利用请求参数实现过滤(含 Waterline 子属性条件修饰符)、分页、排序、字段选择与关联填充(populate)。读完本文你将掌握 Find 蓝图的全部请求参数与行为语义,理解它在 Sails 蓝图系统 中的底层实现(基于 Waterlinefind()),并能在 REST、shortcut、WebSocket(socket)三种触发方式下正确使用它,包括自动订阅(subscribe / auto-watch)带来的实时通知能力。
Find blueprint 是什么
Find blueprint 是 Sails 为每个模型自动生成的"列表查询"端点。只要模型中存在对应的自动路由(REST 蓝图或 shortcut 蓝图),就可以用一次简单的 HTTP 请求查回一组记录:
GET /:model例如项目中有Purchase模型,则GET /purchase会返回数据库中一批购买记录。返回结果可以依据蓝图配置与请求中携带的参数进行过滤(filtering)、分页(pagination)与排序(sorting)。
从源码角度看,Sails 在 blueprints 钩子注册动作 阶段,会为每个模型注册modelIdentity + '/find'动作,其实现位于 Find 蓝图动作源码,核心逻辑非常简洁:
- 调用
parseBlueprintOptions(req)把请求解析为一组 Waterline 查询选项(criteria、populates、meta); - 执行
Model.find(criteria, populates).meta(meta)调用底层 ORM; - 若为 socket 请求则执行订阅逻辑;
- 最终通过
res.ok()返回记录数组。
该端点如何被路由绑定
GET /:model形式的 Find 端点来自两种自动路由机制(默认均开启,见 sails.config.blueprints):
| 路由类型 | 配置项(默认值) | 生成的 URL 模式 |
|---|---|---|
| REST 蓝图 | rest: true | get /:model(例如GET /purchase) |
| Shortcut 蓝图 | shortcuts: true | get /:model/find(例如GET /purchase/find) |
绑定逻辑位于 bindShadowRoutes:REST 路由调用_bindRestRoute('get %s', 'find'),shortcut 路由调用_bindShortcutRoute('get %s/find', 'find')。此外即使自动路由被关闭,你也可以在 自定义路由 中把任意 URL 显式指向该蓝图动作,如'GET /api/purchases': 'purchase/find'。
请求参数详解
Find blueprint 支持以下请求参数,全部为可选项:
| 参数 | 类型 | 说明 |
|---|---|---|
model | ((string)) | 目标模型的 identity。例如GET /purchase中的'purchase'。 |
_*_ | ((string?)) | 以模型属性同名的查询参数进行属性过滤。例如Purchase模型有amount属性,GET /purchase?amount=99.99将返回金额为 $99.99 的购买记录列表。 |
where | ((string?)) | 不按单属性过滤,而是直接给出 Waterline 查询语言 中 WHERE 片段,以 JSON 字符串编码。借助它可以利用contains、startsWith等子属性条件修饰符编写更强大的find()查询。例如?where={"name":{"contains":"theodore"}}。 |
limit | ((number?)) | 最多返回的记录条数(分页用)。默认为 30。例如?limit=100。 |
skip | ((number?)) | 跳过的记录条数(分页用)。例如?skip=30。 |
sort | ((string?)) | 排序规则。默认按主键值升序返回。例如?sort=lastName%20ASC。 |
select | ((string?)) | 结果中每条记录要包含的属性,逗号分隔列表。默认选中全部属性。对 plural("collection")关联属性无效。例如?select=name,age。 |
omit | ((string?)) | 结果中每条记录要排除的属性,逗号分隔列表。不能与select同时使用。对 plural("collection")关联属性无效。例如?omit=favoriteColor,address。 |
populate | ((string)) | 若指定,覆盖默认的自动填充过程。接受逗号分隔的关联属性名列表;传false表示不做任何填充。填充过程如何按模型定义的关联把值填入返回记录,可参见 记录与填充值 相关说明。 |
参数背后的解析逻辑(源码级)
以上参数并非凭空设计,它们与 parseBlueprintOptions 默认实现 中find/findOne分支的处理一一对应,值得理解其边界行为:
where的两种形态:先读取req.allParams().where;若为字符串则尝试JSON.parse()(解析失败会抛出UsageError,最终返回 400)。若未提供where,则把其余未绑定参数组装为 where,并剔除黑名单['limit', 'skip', 'sort', 'populate', 'select', 'omit'],同时丢弃值为undefined的参数。select与omit互斥:源码先判断select存在则设置criteria.select,否则else if才处理omit,因此两者同时发送时omit会被忽略;两者都会按逗号拆分并trim每个属性名。limit默认值:未传limit时固定为DEFAULT_LIMIT = 30(req.param('limit')优先)。skip:仅在显式提供时加入 criteria,默认 0。sort:支持形如lastName ASC的字符串,也支持可JSON.parse的对象形式(如{"name": 1});若字符串不是合法 JSON 则原样解释。相同逻辑还出现在 actionUtil.parseSort。populate:若值为'false'字符串,则populates置空对象(完全不填充);否则按逗号拆分并去空格,生成要填充的关联映射。默认情况下所有collection型关联会带上limit: 30的填充上限(默认 populates 构造),model型关联则无 limit。
完整使用示例
以下示例查找数据库中最新(按创建时间倒序)的至多 30 条购买记录:
GET /purchase?sort=createdAt DESC&limit=30期望响应
返回一个 JSON 数组,例如:
[ { "amount": 49.99, "id": 1, "createdAt": 1485551132315, "updatedAt": 1485551132315 }, { "amount": 99.99, "id": 47, "createdAt": 1485551158349, "updatedAt": 1485551158349 } ]注意:
createdAt/updatedAt为时间戳数字,默认按主键升序排列;当显式传入sort后按排序规则输出。返回体由 find.js 中的res.ok(matchingRecords)统一序列化。
使用 jQuery 调用
$.get('/purchase?sort=createdAt DESC', function (purchases) { console.log(purchases); });使用 sails.io.js(WebSocket 客户端)调用
io.socket.get('/purchase?sort=createdAt DESC', function (purchases) { console.log(purchases); });sails.io.js的完整用法见 sails.io.js 参考文档。
使用 Angular 调用
$http.get('/purchase?sort=createdAt DESC') .then(function (res) { var purchases = res.data; console.log(purchases); });使用 cURL 调用
curl http://localhost:1337/purchase?sort=createdAt%20DESC在 URL 中直接书写空格是不合法的,因此排序参数中的空格需要编码为
%20;在 cURL 示例里即为sort=createdAt%20DESC。
实时能力:socket 请求的自动订阅
Find blueprint 与 WebSocket 深度集成,这是它区别于普通 CRUD 端点的重要特性:
如果该动作是通过 socket 请求触发的,请求方 socket 会被"订阅"到所有返回的记录上。此后若这些记录中的任意一条被更新或删除,一条消息会被发送到该 socket 的客户端,通知它这一变更。
该行为的实现位于 find.js:
if (req._sails.hooks.pubsub && req.isSocket) { Model.subscribe(req, _.pluck(matchingRecords, Model.primaryKey)); // 仅当 `autoWatch` 开启时才 `._watch()` 模型,以感知新建记录 if (req.options.autoWatch) { Model._watch(req); } // 同时对返回记录涉及的所有关联模型实例做深度订阅 _.each(matchingRecords, function (record) { actionUtil.subscribeDeep(req, record); }); }其语义可拆解为三点(底层订阅原语定义在 pubsub 钩子 的subscribe/unsubscribe实现中):
- 订阅返回的记录:对每条返回记录调用
Model.subscribe(req, [pk]),后续这些记录的 update/destroy 事件会推送给当前 socket。详见 Model.subscribe()。 - auto-watch(监听新建):当 sails.config.blueprints.autoWatch 为
true(默认值)时,还会对模型执行_watch(),使 socket 同时收到"新建记录"的通知,从而支持类似实时列表自动刷新的场景。 - 深度订阅关联:
actionUtil.subscribeDeep()(actionUtil.js)会遍历模型关联:对collection型关联,订阅每条关联记录;对model型关联,若填充值是对象则订阅其对应主键。
需要强调的是,如果同一个 socket之后又通过io.socket.put()调用Update或Destroy蓝图,默认情况下不会向该请求方 socket 自身推送消息,而是推送给其他已订阅的 socket。这是刻意设计:客户端 SDK 的回调负责处理服务端响应(例如关闭 loading 动画),而订阅消息则用于通知其他关注者(详见 Blueprint API 与订阅)。需要感知新建时,把autoWatch设为false即可关闭对应通知。
错误处理与状态码
从 find.js 可以看到明确的错误分类:
- 当 Waterline 返回
UsageError(例如传入非法 criteria、whereJSON 解析失败)时,响应400 Bad Request,并借助 formatUsageError 生成更友好的错误信息; - 其他非预期错误一律返回500 Server Error。
因此使用where等高级参数时,务必保证 JSON 语法与属性名正确,否则会得到 400 而不是静默失败。
配置与自定义
常用蓝图配置项
在 config/blueprints.js(仓库对应文档见 sails.config.blueprints)中可调整与 Find 相关的全局行为:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
rest | ((boolean)) | true | 是否启用GET /:model这类 REST 蓝图路由 |
shortcuts | ((boolean)) | true | 是否启用GET /:model/find这类 shortcut 路由(仅建议开发期使用) |
prefix | ((string)) | '' | 所有蓝图路由的挂载前缀,如'/api/v2' |
pluralize | ((boolean)) | false | 是否使用复数模型名(/users对应User模型) |
autoWatch | ((boolean)) | true | 是否在 find/findOne 蓝图动作中订阅新建记录通知 |
parseBlueprintOptions | ((function)) | 默认实现 | 覆盖蓝图动作默认解析行为的钩子函数 |
Sails 1.0 起,
sails.config.blueprints.defaultLimit与sails.config.blueprints.populate已不再受支持(见 blueprints 钩子 configure),默认 limit 固定为 30、默认填充全部关联。如需自定义请使用parseBlueprintOptions。
用 parseBlueprintOptions 覆盖默认行为
Find 蓝图本质上就是"解析请求 → 调用 Waterline 模型方法"。其中Model.find()的查询选项完全由parseBlueprintOptions(req)决定,默认实现可通过sails.hooks.blueprints.parseBlueprintOptions()访问,也允许你在全局或单路由级别覆盖(参考配置文档)。例如限制 Find 请求的limit上限为 100:
// config/blueprints.js module.exports.blueprints = { parseBlueprintOptions: function(req) { // 先取默认查询选项 var queryOptions = req._sails.hooks.blueprints.parseBlueprintOptions(req); // 若是 find / populate 蓝图动作,且请求试图设置过大的 limit,则强制截断为 100 if (req.options.blueprintAction === 'find' || req.options.blueprintAction === 'populate') { if (queryOptions.criteria.limit > 100) { queryOptions.criteria.limit = 100; } } return queryOptions; } };按控制器禁用蓝图
使用传统 controller(而非独立 action 文件)时,可以在控制器内定义_config按控制器关闭对应蓝图路由(该做法仅出于兼容性保留,推荐直接使用自定义路由):
// 在 /api/controllers/PetController.js module.exports = { _config: { actions: false, shortcuts: false, rest: false } }与 FindOne 蓝图的分工
Find 蓝图对应列表查询(GET /:model),而单条查询由 FindOne 蓝图(GET /:model/:id)负责。两者共享同一套参数解析入口,但 findOne.js 会把 criteria 严格裁剪为where/select/omit,且where仅保留主键字段;未找到记录时返回 404,而 Find 始终返回数组(可能为空)。
快速上手:复现本文示例
以下步骤可以完整复现文中所有示例(假设 REST 蓝图开启、项目含Purchase模型):
$ sails new foo $ cd foo $ sails generate model purchase $ sails lift # 会看到数据库自动迁移设置提示。 # 选择 1 (alter) 后按 <ENTER>。启动后访问http://localhost:1337/purchase?sort=createdAt DESC&limit=30即可得到与"期望响应"一致的结果列表。若要验证 socket 订阅,可在项目中引入 sails.io.js 并用io.socket.get()发起请求,随后在其他客户端更新或删除返回的记录,观察 socket 客户端是否收到变更通知。
【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考