news 2026/9/21 1:38:16

Sails 框架 res.json() 完全指南:用法、源码实现与最佳实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sails 框架 res.json() 完全指南:用法、源码实现与最佳实践

Sails 框架 res.json() 完全指南:用法、源码实现与最佳实践

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

导读

res.json()是 Sails(基于 Node.js 的 Realtime MVC 框架)中用于向客户端发送 JSON 响应的核心终端方法,广泛应用于 REST API 控制器、自定义 action 与自定义响应(custom responses)中。本文以官方参考文档为主体,结合 Sails 源码仓库中的实际实现,深入讲解res.json()的用法、与res.send()的区别、底层调用链,以及开发/生产环境下的格式化行为,帮助你写出规范、可复用的 JSON 接口代码。


一、基本用法与签名

1.1 语法

return res.json(data);

其中data是你希望发送给客户端的任意数据。该方法的全名是全小写res.json(),不要误写成res.JSON()res.Json()

1.2 方法特性

  • 发送 JSON 响应res.json()将指定的data序列化为 JSON 字符串并作为响应体发送;
  • 终端方法(terminal):与 Sails 中大多数响应方法一样,res.json()一般应作为某个请求处理流程中的最后一行代码执行,因此官方文档统一建议通过return res.json(data)的形式调用,既保证响应只发送一次,也避免后续代码继续执行产生二次响应。

二、核心行为:与res.send()的区别

官方文档明确指出:

当传入的是对象(object)或数组(array)时,res.json()res.send()行为完全一致。但与res.send()不同的是,res.json()还可以对非对象类型(如nullundefined、数字、字符串等)进行显式 JSON 转换——尽管这些值在严格意义上并不是合法的 JSON。

从源码层面看,这一行为在 lib/router/res.js 的_jsonShim实现中有直观体现:

res.json = res.json || function _jsonShim (data, noLongerSupported) { if (!_.isUndefined(noLongerSupported)) { throw new Error('The 2-ary usage of `res.json()` is no longer supported in Express 4/Sails v1. Please use `res.status(statusCode).json(body)` instead.'); } // If data is a string, JSON stringify it. // (Otherwise, we can just rely on `send` to do that for us.) if (_.isString(data)) { data = JSON.stringify(data); res.set('content-type', 'application/json'); } return res.status(res.statusCode || 200).send(data); };

从上述实现可以提炼出三个关键点:

  1. 字符串数据会被强制 JSON 序列化:如果传入字符串,res.json()会先对它执行JSON.stringify()(相当于加了一层引号),并显式设置Content-Type: application/json;而res.send()对字符串是原样发送、不设置 JSON 头;
  2. 最终委托给res.send()res.json()本质上是res.send()的包装,对象/数组由res.send()内部的JSON.stringify完成序列化(见 lib/router/res.js);
  3. 默认状态码为 200res.status(res.statusCode || 200)保证在未显式设置状态码时返回 200。

2.1 序列化失败的兜底

res.send()的内部实现中,如果JSON.stringify(data)抛错(例如数据包含循环引用),框架会捕获异常并给出明确的报错信息,同时将响应状态码置为 500(见 lib/router/res.js),避免应用静默失败。


三、实战示例(完整继承官方用例)

3.1 发送一个对象

return res.json({ firstName: 'Tobi' });

响应结果:

{ "firstName": "Tobi" }

3.2 与res.status()链式调用,指定状态码

return res.status(201).json({ id: 201721 });

res.status()返回res自身以支持链式调用(见 lib/router/res.js),因此可以先设置 201(Created)状态码,再发送 JSON 响应体。

3.3 发送原始类型(primitive)

结合 Waterline 数据查询的典型场景:

var leena = await User.findOne({ firstName: 'Leena' }); if (!leena) { return res.notFound(); } return res.json(leena.id);//« you can send down primitives, like numbers

需要注意:虽然res.json()允许发送数字这类原始类型(响应体会是123这样的裸数字),但这在严格 JSON 规范下并不完全合法,实践中更推荐发送对象:

return res.json({ id: leena.id });

3.4 发送数组

return res.json([ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' } ]);

四、从源码看res.json()的完整调用链

在 Sails v1 中,res.json()并不是凭空存在的方法,而是 Sails 在虚拟请求解释器(virtual request interpreter)中为res对象补齐的一组"shim"之一。其完整调用链如下:

  1. 请求进入 Sails 路由层后,res对象经由 lib/router/res.js 构建——若底层没有真实的 Node HTTP 响应流,则使用MockRes,并注入res.status()res.send()res.json()等方法的 shim 实现;
  2. 应用代码调用return res.json(data)
  3. _jsonShim对字符串做特殊处理(JSON 序列化 + 设置 JSON 头),其余情况交给res.send()
  4. res.send()负责:禁止二次响应(onlyAllowOneResponse,见 lib/router/res.js)、确保字符集为utf-8、序列化非字符串数据、写入响应流并end()
  5. 若使用了虚拟请求解释器(如通过sails.request()发起请求,见 lib/app/request.js),数据会被缓冲到res._clientRes.body供回调读取(见 lib/router/res.js)。

4.1 幂等性与"一次响应"保护

onlyAllowOneResponse()会在首次发送响应时置位_virtualResponseStarted,此后再次调用res.send()/res.json()会抛出'Cannot write to response more than once'错误。这就是官方文档强调用return包裹调用的根本原因——防止同一次请求中意外发送多次响应。


五、开发环境下的 JSON 美化输出

Sails 在 HTTP 层初始化时(lib/hooks/http/initialize.js)针对res.json()的输出做了环境相关处理:

// In non-production environments, format `res.json()` output nicely. if (process.env.NODE_ENV !== 'production') { expressApp.set('json spaces', 2); }

也就是说:

  • 开发环境NODE_ENV !== 'production'):JSON 响应会以 2 空格缩进的美化格式输出,方便调试与阅读;
  • 生产环境NODE_ENV === 'production'):不设置json spaces,输出压缩后的紧凑 JSON,节省带宽。

六、res.json()与 Sails 自定义响应方法的关系

Sails 内置的多个自定义响应方法(custom responses)底层都调用了res.json(),它们是对res.json()更高层语义的封装:

方法状态码底层行为源码位置
res.ok()200无数据时走sendStatus(200),有数据时调用res.json(data)lib/hooks/responses/defaults/ok.js
res.badRequest()400最终调用res.json(data)lib/hooks/responses/defaults/badRequest.js
res.serverError()500根据环境调用res.json(data)或发送精简消息lib/hooks/responses/defaults/serverError.js

res.ok()为例(lib/hooks/responses/defaults/ok.js),它对传入的Error实例做了防御处理:若 Error 没有自定义toJSON(),开发环境下用util.inspect()输出可读信息,生产环境下直接sendStatus(200),避免res.json()把 Error 序列化成一个空字典{}。这说明:当你需要自定义响应时,理解res.json()的序列化行为是前提

此外,历史遗留的res.jsonx()在 Sails v1.0 中已被标记为废弃(见 lib/hooks/responses/index.js),官方建议统一使用res.json()(或res.jsonp()处理 JSONP 场景)。


七、注意事项与常见误区

  1. 方法名全小写res.json()中的json必须小写,这是 Express/Sails 的既定约定;
  2. 终端方法:它是某个请求处理流程的终点,务必配合return使用,防止后续代码继续执行;
  3. 不要再传第二个参数:Express 4 / Sails v1 起,res.json(statusCode, data)这种二参数用法已不再支持,调用会直接抛错。正确写法是先res.status(statusCode).json(data)(见 lib/router/res.js);
  4. 不要重复发送响应res.json()之后再次调用任何发送型方法会触发'Cannot write to response more than once'错误;
  5. 优先发送对象:虽然可以发送数字等原始类型,但从 JSON 规范与客户端解析健壮性考虑,推荐统一发送对象或数组;
  6. 发送错误对象需谨慎:直接res.json(err)可能把Error实例序列化成空字典,建议使用res.serverError()或先提取err.message

八、延伸阅读

  • res.json()最常搭配的状态码设置方法:res.status();
  • res.json()的底层基座方法(字符串/XML/CSV 等非 JSON 响应):res.send();
  • 在 REST API 中更语义化的响应封装:res.ok()、res.notFound()、res.serverError();
  • 自定义响应方法的位置与用法:Custom Responses。

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

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

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

零基础选AI软件:先搞懂三件事,按场景匹配不踩坑

1. 先别急着下载:零基础选 AI 软件,先搞懂三件事这段时间我收到特别多类似的提问:大家都是零基础,看到网上铺天盖地的 AI 软件推荐,脑子里全是问号。某某 AI 能做 PPT,某某 AI 能写代码,某某 AI…

作者头像 李华
网站建设 2026/9/21 1:34:44

XVF3800远场语音前端原型搭建:从选型到调试的完整避坑指南

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

作者头像 李华
网站建设 2026/9/21 1:30:51

星网锐捷语音网关密码重置与恢复出厂设置实战指南

语音网关这类设备,平时安安静静待在机柜角落里,一旦管理员密码丢了、或者配置被改乱到进不去后台,那种“看得见摸不着”的焦虑感,做过运维的人都懂。星网锐捷的语音网关在企业办公、呼叫中心、酒店话务这些场景里铺得很广&#xf…

作者头像 李华
网站建设 2026/9/21 1:30:45

纯文本模型识图失败?TaoToken 这样改 claude-vision-skill 的环境变量

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

作者头像 李华