从 v1.0 版本开始,所有 Sails 应用都内置支持**辅助函数**,这些简单的实用程序允许您在多个地方共享 Node.js 代码。这有助于避免重复代码,并通过减少错误和最大程度地减少重写来提高开发效率。与 action2 类似,这也使得为您的应用创建文档变得更加容易。
在 Sails 中,辅助函数是将重复代码提取到单独文件中,然后在各种action、自定义响应、命令行脚本、单元测试甚至其他辅助函数中重用该代码的推荐方法。您**不必**使用辅助函数——事实上,您可能一开始甚至不需要它们。但是,随着代码库的增长,辅助函数对于应用程序的可维护性将变得越来越重要(此外,它们非常方便)。
例如,在创建 Node.js/Sails 应用用于响应客户端请求的 action 过程中,您有时会发现自己在多个地方重复代码。当然,这很容易出错,更不用说令人烦恼了。幸运的是,有一个巧妙的解决方案:用对自定义辅助函数的调用替换重复代码。
const greeting = await sails.helpers.formatWelcomeMessage('Bubba');
sails.log(greeting);
// => "Hello, Bubba!"
只要该位置可以访问
sails
应用实例,就可以从代码中的几乎任何地方调用辅助函数。
这是一个简单且定义良好的辅助函数示例
// api/helpers/format-welcome-message.js
module.exports = {
friendlyName: 'Format welcome message',
description: 'Return a personalized greeting based on the provided name.',
inputs: {
name: {
type: 'string',
example: 'Ami',
description: 'The name of the person to greet.',
required: true
}
},
fn: async function (inputs, exits) {
const result = `Hello, ${inputs.name}!`;
return exits.success(result);
}
};
虽然简单,但此文件显示了良好辅助函数的几个特征:它以友好的名称和描述开头,使您立即清楚该实用程序的功能,它描述了其输入,以便于查看如何使用该实用程序,并且它以尽可能简单的方式完成一项离散的任务。
fn
函数辅助函数的核心是fn
函数,其中包含辅助函数将运行的实际代码。该函数接受两个参数:inputs
(输入值的字典,或“argins”)和exits
(回调函数的字典)。fn
的工作是利用和处理 argins,然后触发提供的其中一个 exit 将控制权返回给调用辅助函数的任何代码。请注意,与使用return
向调用方提供输出的典型 JavaScript 函数相反,辅助函数通过将其传递给exits.success()
来提供该结果值。
辅助函数声明的输入类似于典型 JavaScript 函数的参数:它们定义了代码必须使用的值。但是,与标准的 JavaScript 函数参数不同,输入会自动进行验证。如果使用与其对应输入类型错误的 argins 调用辅助函数,或者缺少必需输入的值,它将触发错误。因此,辅助函数是自验证的。
辅助函数的输入在inputs
字典中定义。每个输入定义至少包含一个type
属性。辅助函数输入支持以下类型:
string
- 字符串值number
- 数字值(整数和浮点数均有效)boolean
- 值true
或false
ref
- JavaScript 变量引用(可以是任何值,包括字典、数组、函数、流等)这些与您可能已经习惯于定义模型属性的数据类型(以及相关语义)相同。因此,正如您所料,您可以通过设置其defaultsTo
属性为输入提供默认值。或者,您可以通过设置required: true
将其设为必需。您甚至可以使用allowNull
和几乎所有更高级别的验证规则,例如isEmail
。
在调用辅助函数时传递的参数与该辅助函数声明的inputs
中键的顺序相对应。或者,如果您希望按名称传递 argins,请使用.with()
const greeting = await sails.helpers.formatWelcomeMessage.with({ name: 'Bubba' });
退出描述了辅助函数可能具有的所有不同结果,无论好坏。每个辅助函数都自动支持error
和success
退出。在调用辅助函数时,如果其fn
触发success
,则它将正常返回。但如果其fn
触发了除success
之外的其他 exit,则它将抛出错误(除非使用了.tolerate()
)。
如有必要,您还可以公开其他自定义退出(称为“异常”),允许调用辅助函数的用户代码处理特定的异常情况。这有助于通过使声明和协商错误变得轻松简单来保证代码的透明度和可维护性。
辅助函数的异常(自定义退出)在
exits
字典中定义。最好为所有自定义异常提供显式的description
属性。
想象一个名为“inviteNewUser”的辅助函数,它公开了一个自定义的emailAddressInUse
退出。如果提供的电子邮件已存在,则辅助函数的fn
可能会触发此自定义退出,允许您的用户代码处理此特定场景——而不会弄乱您的结果值或诉诸额外的try/catch
块。
例如,如果从具有自己的“badRequest”退出的 action 中调用此辅助函数
const newUserId = sails.helpers.inviteNewUser('[email protected]')
.intercept('emailAddressInUse', 'badRequest');
上面的花哨速记只是编写以下代码的更快方法
.intercept('emailAddressInUse', (err)=>{ return 'badRequest'; });
至于.intercept(),它只是另一个快捷方式,因此您不必每次都手动编写自定义的 try/catch 块来协商这些错误。
在内部,辅助函数的fn
负责触发其退出之一,方法是抛出特殊的退出信号或调用退出回调(例如exits.success('foo')
)。如果辅助函数通过 success 退出发送回结果(例如'foo'
),则这将成为辅助函数的返回值。
注意:对于非 success 退出,Sails 将使用 exit 的预定义描述自动创建适当的 JavaScript Error 实例(如果需要)。
默认情况下,所有辅助函数都被视为异步的。虽然这是一个安全的默认假设,但并非总是如此。当您确定辅助函数是同步的时,您可以使用sync: true
属性告诉 Sails 来优化性能。这允许用户代码在不使用await
的情况下调用辅助函数。但是,如果您将sync
设置为true
,请不要忘记将fn: async function
更改为fn: function
!
注意:在不使用
await
的情况下调用异步辅助函数将不起作用。
req
如果您正在设计一个专门用于在 action 中使用的辅助函数来解析请求标头,那么您需要利用请求对象的预先存在的 method 和/或属性。允许 action 中的代码将req
传递给辅助函数的最简单方法是定义一个type: 'ref'
输入
inputs: {
req: {
type: 'ref',
description: 'The current incoming request (req).',
required: true
}
}
然后,要在 action 中使用辅助函数,您可以编写如下代码
const headers = await sails.helpers.parseMyHeaders(req);
Sails 提供了一个内置生成器,您可以使用它来自动创建一个新的辅助函数
sails generate helper foo-bar
这将创建一个文件api/helpers/foo-bar.js
,可以在代码中以sails.helpers.fooBar
的形式访问。最初创建的文件将是一个通用的辅助函数,没有输入,只有默认退出(success
和error
),在执行时立即触发其success
退出。
每当 Sails 应用加载时,它都会找到api/helpers/
中的所有文件,将其编译成函数,并使用文件名的驼峰式版本将其存储在sails.helpers
字典中。然后,可以通过简单地使用await
调用它并提供一些 argin 值,从代码中的任何位置调用任何辅助函数。
const result = await sails.helpers.formatWelcomeMessage('Dolly');
sails.log('Ok it worked! The result is:', result);
这与您可能已经熟悉的模型方法(如
.create()
)的使用方式大致相同。
如果辅助函数声明了sync
属性,您也可以在不使用await
的情况下调用它。
const greeting = sails.helpers.formatWelcomeMessage('Timothy');
但在删除await
之前,请确保辅助函数确实是同步的。如果没有await
,异步辅助函数将永远不会执行!
如果您的应用程序使用了许多辅助函数,您可能会发现将相关的辅助函数分组到子目录中很有帮助。例如,假设您有一些user
辅助函数和一些item
辅助函数,它们按以下目录结构组织
api/
helpers/
user/
find-by-username.js
toggle-admin-role.js
validate-username.js
item/
set-price.js
apply-coupon.js
在调用这些辅助函数时,每个子文件夹名称(例如user
和item
)都会在sails.helpers
对象中成为一个额外的属性层,因此您可以使用sails.helpers.user.findByUsername()
调用find-by-username.js
,并且可以使用sails.helpers.item.setPrice()
调用set-price.js
。
有关更多信息,您可以阅读Ryan Emberling 和 Mike McNeil 之间的对话,其中详细介绍了此用例,包括一些使用自定义辅助函数和有机物的通用技巧。
为了更细粒度的错误处理(甚至对于那些不完全是错误的异常情况),您可能习惯于设置某种错误代码,然后嗅探错误。这种方法可以正常工作,但可能很耗时且难以跟踪。
幸运的是,在 Sails 辅助函数中有一些不同的方法可以方便地处理错误。有关更多信息,请参阅有关.tolerate()、.intercept()和特殊退出信号的页面。
虽然此示例中的用法过于冗长,但很容易想象在依赖于notUnique
等自定义退出时会很有帮助的场景。尽管如此,您不希望每次都必须处理每个自定义退出。理想情况下,您只需要在必要时在用户代码中处理自定义退出:无论是实现某种功能,还是改善用户体验或提供更好的内部错误消息。
幸运的是,Sails 助手支持“自动退出转发”。这意味着用户代码可以选择根据具体情况与**任意数量的自定义退出**进行集成。换句话说,在调用助手时,如果不需要其自定义的notUnique
退出,完全可以忽略它。这样,您的代码可以保持尽可能简洁和直观。如果情况发生变化,您始终可以修改代码来稍后处理自定义退出。
sails-hook-organics
(捆绑在“Web App”模板中)包含多个免费、开源且具有 MIT 许可证的助手,用于许多常见用例。看看吧!