news 2026/9/20 18:39:58

Sails Find 蓝图(Blueprint)完全指南:列表查询、过滤、分页、排序与实时订阅

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sails Find 蓝图(Blueprint)完全指南:列表查询、过滤、分页、排序与实时订阅

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 蓝图动作源码,核心逻辑非常简洁:

  1. 调用parseBlueprintOptions(req)把请求解析为一组 Waterline 查询选项(criteria、populates、meta);
  2. 执行Model.find(criteria, populates).meta(meta)调用底层 ORM;
  3. 若为 socket 请求则执行订阅逻辑;
  4. 最终通过res.ok()返回记录数组。

该端点如何被路由绑定

GET /:model形式的 Find 端点来自两种自动路由机制(默认均开启,见 sails.config.blueprints):

路由类型配置项(默认值)生成的 URL 模式
REST 蓝图rest: trueget /:model(例如GET /purchase
Shortcut 蓝图shortcuts: trueget /: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 字符串编码。借助它可以利用containsstartsWith等子属性条件修饰符编写更强大的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的参数。
  • selectomit互斥:源码先判断select存在则设置criteria.select,否则else if才处理omit,因此两者同时发送时omit会被忽略;两者都会按逗号拆分并trim每个属性名。
  • limit默认值:未传limit时固定为DEFAULT_LIMIT = 30req.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实现中):

  1. 订阅返回的记录:对每条返回记录调用Model.subscribe(req, [pk]),后续这些记录的 update/destroy 事件会推送给当前 socket。详见 Model.subscribe()。
  2. auto-watch(监听新建):当 sails.config.blueprints.autoWatch 为true(默认值)时,还会对模型执行_watch(),使 socket 同时收到"新建记录"的通知,从而支持类似实时列表自动刷新的场景。
  3. 深度订阅关联actionUtil.subscribeDeep()(actionUtil.js)会遍历模型关联:对collection型关联,订阅每条关联记录;对model型关联,若填充值是对象则订阅其对应主键。

需要强调的是,如果同一个 socket之后又通过io.socket.put()调用UpdateDestroy蓝图,默认情况下不会向该请求方 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.defaultLimitsails.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),仅供参考

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

具身智能入门:从ROS2机器人仿真到真实设备部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

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

电动汽车四轮独立驱动技术解析:从底盘构型到控制算法

简介&#xff1a;电动汽车四轮独立驱动技术.pdf是一份聚焦电动汽车新型驱动架构的技术类PDF&#xff0c;面向汽车工程、机电一体化及控制领域的学生、研发人员。文档系统梳理了四轮独立驱动技术的核心优势、系统组成与控制策略&#xff0c;从绪论到具体技术展开&#xff0c;重点…

作者头像 李华
网站建设 2026/9/20 18:26:12

LLVM核心架构与llvmpipe向量化:从源码构建到实战优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华