辅助函数

辅助函数参考

AdonisJS 将其工具函数打包到 helpers 模块中,并使它们对你的应用代码可用。由于这些工具函数已经被框架安装并使用,helpers 模块不会为你的 node_modules 增加任何额外的体积。

辅助方法从以下模块导出。

import is from '@adonisjs/core/helpers/is'
import * as helpers from '@adonisjs/core/helpers'
import string from '@adonisjs/core/helpers/string'

escapeHTML

转义字符串值中的 HTML 实体。在底层,我们使用 he 包。

import string from '@adonisjs/core/helpers/string'
string.escapeHTML('<p> foo © bar </p>')
// &lt;p&gt; foo © bar &lt;/p&gt;

可选地,你可以使用 encodeSymbols 选项对非 ASCII 符号进行编码。

import string from '@adonisjs/core/helpers/string'
string.escapeHTML('<p> foo © bar </p>', {
encodeSymbols: true,
})
// &lt;p&gt; foo &#xA9; bar &lt;/p&gt;

encodeSymbols

你可以使用 encodeSymbols 辅助函数对字符串值中的非 ASCII 符号进行编码。在底层,我们使用 he.encode 方法。

import string from '@adonisjs/core/helpers/string'
string.encodeSymbols('foo © bar ≠ baz 𝌆 qux')
// 'foo &#xA9; bar &#x2260; baz &#x1D306; qux'

prettyHrTime

美观打印 process.hrtime 方法的差值。

import { hrtime } from 'node:process'
import string from '@adonisjs/core/helpers/string'
const startTime = hrtime()
await someOperation()
const endTime = hrtime(startTime)
console.log(string.prettyHrTime(endTime))

isEmpty

检查字符串值是否为空。

import string from '@adonisjs/core/helpers/string'
string.isEmpty('') // true
string.isEmpty(' ') // true

truncate

在给定字符数处截断字符串。

import string from '@adonisjs/core/helpers/string'
string.truncate('This is a very long, maybe not that long title', 12)
// 输出:This is a ve...

默认情况下,字符串恰好在给定索引处被截断。不过,你可以指示该方法等待单词完结后再截断。

string.truncate('This is a very long, maybe not that long title', 12, {
completeWords: true,
})
// 输出:This is a very...

你可以使用 suffix 选项自定义后缀。

string.truncate('This is a very long, maybe not that long title', 12, {
completeWords: true,
suffix: '... <a href="/1"> Read more </a>',
})
// 输出:This is a very... <a href="/1"> Read more </a>

excerpt

excerpt 方法与 truncate 方法相同。不过,它会从字符串中去除 HTML 标签。

import string from '@adonisjs/core/helpers/string'
string.excerpt('<p>This is a <strong>very long</strong>, maybe not that long title</p>', 12, {
completeWords: true,
})
// 输出:This is a very...

slug

为字符串值生成 slug。该方法从 slugify 包 导出,因此请查阅其文档了解可用选项。

import string from '@adonisjs/core/helpers/string'
console.log(string.slug('hello ♥ world'))
// hello-love-world

你可以如下为 Unicode 值添加自定义替换规则。

string.slug.extend({ '': 'radioactive' })
console.log(string.slug('unicode ♥ is ☢'))
// unicode-love-is-radioactive

interpolate

在字符串中插值变量。变量必须位于双花括号内。

import string from '@adonisjs/core/helpers/string'
string.interpolate('hello {{ user.username }}', {
user: {
username: 'virk'
}
})
// hello virk

花括号可以使用 \\ 前缀进行转义。

string.interpolate('hello \\{{ users.0 }}', {})
// hello {{ users.0 }}

plural

将一个单词转换为其复数形式。该方法从 pluralize 包 导出。

import string from '@adonisjs/core/helpers/string'
string.plural('test')
// tests

isPlural

判断一个单词是否已经是复数形式。

import string from '@adonisjs/core/helpers/string'
string.isPlural('tests') // true

pluralize

此方法组合了 singularplural 方法,并根据数量选择使用其中之一。例如:

import string from '@adonisjs/core/helpers/string'
string.pluralize('box', 1) // box
string.pluralize('box', 2) // boxes
string.pluralize('box', 0) // boxes
string.pluralize('boxes', 1) // box
string.pluralize('boxes', 2) // boxes
string.pluralize('boxes', 0) // boxes

pluralize 属性导出了 额外的方法 来注册自定义不可数、不规则、复数和单数的规则。

import string from '@adonisjs/core/helpers/string'
string.pluralize.addUncountableRule('paper')
string.pluralize.addSingularRule(/singles$/i, 'singular')

singular

将一个单词转换为其单数形式。该方法从 pluralize 包 导出。

import string from '@adonisjs/core/helpers/string'
string.singular('tests')
// test

isSingular

判断一个单词是否已经是单数形式。

import string from '@adonisjs/core/helpers/string'
string.isSingular('test') // true

camelCase

将字符串值转换为驼峰命名(camelCase)。

import string from '@adonisjs/core/helpers/string'
string.camelCase('user_name') // userName

以下是一些转换示例。

输入输出
'test''test'
'test string''testString'
'Test String''testString'
'TestV2''testV2'
'foo_bar''fooBar'
'version 1.2.10''version1210'
'version 1.21.0''version1210'

capitalCase

将字符串值转换为首字母大写形式(capital case)。

import string from '@adonisjs/core/helpers/string'
string.capitalCase('helloWorld') // Hello World

以下是一些转换示例。

输入输出
'test''Test'
'test string''Test String'
'Test String''Test String'
'TestV2''Test V 2'
'version 1.2.10''Version 1.2.10'
'version 1.21.0''Version 1.21.0'

dashCase

将字符串值转换为短横线命名(dash case)。

import string from '@adonisjs/core/helpers/string'
string.dashCase('helloWorld') // hello-world

可选地,你可以将每个单词的首字母大写。

string.dashCase('helloWorld', { capitalize: true }) // Hello-World

以下是一些转换示例。

输入输出
'test''test'
'test string''test-string'
'Test String''test-string'
'Test V2''test-v2'
'TestV2''test-v-2'
'version 1.2.10''version-1210'
'version 1.21.0''version-1210'

dotCase

将字符串值转换为点分隔命名(dot case)。

import string from '@adonisjs/core/helpers/string'
string.dotCase('helloWorld') // hello.World

可选地,你可以将所有单词的首字母转换为小写。

string.dotCase('helloWorld', { lowerCase: true }) // hello.world

以下是一些转换示例。

输入输出
'test''test'
'test string''test.string'
'Test String''Test.String'
'dot.case''dot.case'
'path/case''path.case'
'TestV2''Test.V.2'
'version 1.2.10''version.1210'
'version 1.21.0''version.1210'

noCase

去除字符串值中的所有命名风格。

import string from '@adonisjs/core/helpers/string'
string.noCase('helloWorld') // hello world

以下是一些转换示例。

输入输出
'test''test'
'TEST''test'
'testString''test string'
'testString123''test string123'
'testString_1_2_3''test string 1 2 3'
'ID123String''id123 string'
'foo bar123''foo bar123'
'a1bStar''a1b star'
'CONSTANT_CASE ''constant case'
'CONST123_FOO''const123 foo'
'FOO_bar''foo bar'
'XMLHttpRequest''xml http request'
'IQueryAArgs''i query a args'
'dot.case''dot case'
'path/case''path case'
'snake_case''snake case'
'snake_case123''snake case123'
'snake_case_123''snake case 123'
'"quotes"''quotes'
'version 0.45.0''version 0 45 0'
'version 0..78..9''version 0 78 9'
'version 4_99/4''version 4 99 4'
' test ''test'
'something_2014_other''something 2014 other'
'amazon s3 data''amazon s3 data'
'foo_13_bar''foo 13 bar'

pascalCase

将字符串值转换为帕斯卡命名(Pascal case)。非常适合用于生成 JavaScript 类名。

import string from '@adonisjs/core/helpers/string'
string.pascalCase('user team') // UserTeam

以下是一些转换示例。

输入输出
'test''Test'
'test string''TestString'
'Test String''TestString'
'TestV2''TestV2'
'version 1.2.10''Version1210'
'version 1.21.0''Version1210'

sentenceCase

将一个值转换为句子形式。

import string from '@adonisjs/core/helpers/string'
string.sentenceCase('getting_started-with-adonisjs')
// Getting started with adonisjs

以下是一些转换示例。

输入输出
'test''Test'
'test string''Test string'
'Test String''Test string'
'TestV2''Test v2'
'version 1.2.10''Version 1 2 10'
'version 1.21.0''Version 1 21 0'

snakeCase

将值转换为蛇形命名(snake case)。

import string from '@adonisjs/core/helpers/string'
string.snakeCase('user team') // user_team

以下是一些转换示例。

输入输出
'_id''id'
'test''test'
'test string''test_string'
'Test String''test_string'
'Test V2''test_v2'
'TestV2''test_v_2'
'version 1.2.10''version_1210'
'version 1.21.0''version_1210'

titleCase

将字符串值转换为标题大小写(title case)。

import string from '@adonisjs/core/helpers/string'
string.titleCase('small word ends on')
// Small Word Ends On

以下是一些转换示例。

输入输出
'one. two.''One. Two.'
'a small word starts''A Small Word Starts'
'small word ends on''Small Word Ends On'
'we keep NASA capitalized''We Keep NASA Capitalized'
'pass camelCase through''Pass camelCase Through'
'follow step-by-step instructions''Follow Step-by-Step Instructions'
'this vs. that''This vs. That'
'this vs that''This vs That'
'newcastle upon tyne''Newcastle upon Tyne'
'newcastle *upon* tyne''Newcastle *upon* Tyne'

random

生成给定长度的加密安全随机字符串。输出值是一个 URL 安全的 base64 编码字符串。

import string from '@adonisjs/core/helpers/string'
string.random(32)
// 8mejfWWbXbry8Rh7u8MW3o-6dxd80Thk

sentence

将一个单词数组转换为以逗号分隔的句子。

import string from '@adonisjs/core/helpers/string'
string.sentence(['routes', 'controllers', 'middleware'])
// routes, controllers, and middleware

你可以通过指定 options.lastSeparator 属性,用 or 替换 and

string.sentence(['routes', 'controllers', 'middleware'], {
lastSeparator: ', or ',
})

在以下示例中,这两个单词使用 and 分隔符组合,而不是逗号(通常在英语中提倡使用逗号)。不过,你可以为两个单词使用自定义分隔符。

string.sentence(['routes', 'controllers'])
// routes and controllers
string.sentence(['routes', 'controllers'], {
pairSeparator: ', and ',
})
// routes, and controllers

condenseWhitespace

将字符串中的多个空白字符压缩为单个空白字符。

import string from '@adonisjs/core/helpers/string'
string.condenseWhitespace('hello world')
// hello world
string.condenseWhitespace(' hello world ')
// hello world

seconds

将基于字符串的时间表达式解析为秒数。

import string from '@adonisjs/core/helpers/string'
string.seconds.parse('10h') // 36000
string.seconds.parse('1 day') // 86400

parse 方法传入数值会原样返回,假定该值已经是秒数。

string.seconds.parse(180) // 180

你可以使用 format 方法将秒数格式化为美观的字符串。

string.seconds.format(36000) // 10h
string.seconds.format(36000, true) // 10 hours

milliseconds

将基于字符串的时间表达式解析为毫秒。

import string from '@adonisjs/core/helpers/string'
string.milliseconds.parse('1 h') // 3.6e6
string.milliseconds.parse('1 day') // 8.64e7

parse 方法传入数值会原样返回,假定该值已经是毫秒。

string.milliseconds.parse(180) // 180

使用 format 方法,你可以将毫秒格式化为美观的字符串。

string.milliseconds.format(3.6e6) // 1h
string.milliseconds.format(3.6e6, true) // 1 hour

bytes

将基于字符串的单位表达式解析为字节。

import string from '@adonisjs/core/helpers/string'
string.bytes.parse('1KB') // 1024
string.bytes.parse('1MB') // 1048576

parse 方法传入数值会原样返回,假定该值已经是字节。

string.bytes.parse(1024) // 1024

使用 format 方法,你可以将字节格式化为美观的字符串。该方法直接从 bytes 包导出。可用选项请参考该包的 README。

string.bytes.format(1048576) // 1MB
string.bytes.format(1024 * 1024 * 1000) // 1000MB
string.bytes.format(1024 * 1024 * 1000, { thousandsSeparator: ',' }) // 1,000MB

ordinal

获取给定数字的序数后缀。

import string from '@adonisjs/core/helpers/string'
string.ordinal(1) // 1st
string.ordinal(2) // '2nd'
string.ordinal(3) // '3rd'
string.ordinal(4) // '4th'
string.ordinal(23) // '23rd'
string.ordinal(24) // '24th'

safeEqual

检查两个 buffer 或字符串值是否相同。此方法不会泄露任何时序信息,能够防止时序攻击

在底层,此方法使用 Node.js 的 crypto.timeSafeEqual 方法,并支持比较字符串值。(crypto.timeSafeEqual 不支持字符串比较)

import { safeEqual } from '@adonisjs/core/helpers'
/**
* 受信任的值,它可能保存在数据库中
*/
const trustedValue = 'hello world'
/**
* 不受信任的用户输入
*/
const userInput = 'hello'
if (safeEqual(trustedValue, userInput)) {
// 两者相同
} else {
// 值不匹配
}

cuid

创建一个安全的、抗碰撞的 ID,针对水平扩展和性能进行了优化。此方法在底层使用 @paralleldrive/cuid2 包。

import { cuid } from '@adonisjs/core/helpers'
const id = cuid()
// tz4a98xxat96iws9zmbrgj3a

你可以使用 isCuid 方法来检查一个值是否为有效的 CUID。

import { cuid, isCuid } from '@adonisjs/core/helpers'
const id = cuid()
isCuid(id) // true

compose

compose 辅助函数允许你以更简洁的 API 使用 TypeScript 类混入(mixins)。以下是未使用 compose 辅助函数时的混入用法示例。

class User extends UserWithAttributes(UserWithAge(UserWithPassword(UserWithEmail(BaseModel)))) {}

以下是使用 compose 辅助函数时的示例。

  • 没有嵌套。
  • 混入的顺序是从(左到右/从上到下)。而之前是从内到外。
import { compose } from '@adonisjs/core/helpers'
class User extends compose(
BaseModel,
UserWithEmail,
UserWithPassword,
UserWithAge,
UserWithAttributes
) {}

base64

用于 base64 编码和解码值的工具方法。

import { base64 } from '@adonisjs/core/helpers'
base64.encode('hello world')
// aGVsbG8gd29ybGQ=

encode 方法类似,你可以使用 urlEncode 生成可在 URL 中安全传递的 base64 字符串。

urlEncode 方法执行以下替换。

  • + 替换为 -
  • / 替换为 _
  • 并从字符串末尾移除 = 符号。
base64.urlEncode('hello world')
// aGVsbG8gd29ybGQ

你可以使用 decodeurlDecode 方法解码先前编码的 base64 字符串。

base64.decode(base64.encode('hello world'))
// hello world
base64.urlDecode(base64.urlEncode('hello world'))
// hello world

当输入值不是有效的 base64 字符串时,decodeurlDecode 方法会返回 null。你可以开启 strict 模式来改为抛出异常。

base64.decode('hello world') // null
base64.decode('hello world', 'utf-8', true) // 抛出异常

fsReadAll

获取目录中所有文件的列表。该方法会递归地从主文件夹和子文件夹中获取文件。点文件(dotfiles)会被隐式忽略。

import { fsReadAll } from '@adonisjs/core/helpers'
const files = await fsReadAll(new URL('./config', import.meta.url), { pathType: 'url' })
await Promise.all(files.map((file) => import(file)))

你也可以将选项连同目录路径一起作为第二个参数传入。

type Options = {
ignoreMissingRoot?: boolean
filter?: (filePath: string, index: number) => boolean
sort?: (current: string, next: string) => number
pathType?: 'relative' | 'unixRelative' | 'absolute' | 'unixAbsolute' | 'url'
}
const options: Partial<Options> = {}
await fsReadAll(location, options)
参数描述
ignoreMissingRoot默认情况下,当根目录缺失时会抛出异常。将 ignoreMissingRoot 设置为 true 不会导致错误,而是会返回空数组。
filter定义一个过滤器来忽略某些路径。该方法会在最终的文件列表上被调用。
sort定义一个自定义方法来对文件路径排序。默认情况下,文件使用自然排序(natural sort)。
pathType定义如何返回收集到的路径。默认返回与操作系统相关的相对路径。如果你想导入收集到的文件,必须将 pathType = 'url'

fsImportAll

fsImportAll 方法递归地导入给定目录中的所有文件,并将每个模块导出的值设置到一个对象上。

import { fsImportAll } from '@adonisjs/core/helpers'
const collection = await fsImportAll(new URL('./config', import.meta.url))
console.log(collection)
  • collection 是一个包含键值对树的对象。
  • 键是从文件路径创建的嵌套对象。
  • 值是模块导出的内容。如果一个模块同时有 defaultnamed 导出,则只使用默认导出。

第二个参数是用于自定义导入行为的选项。

type Options = {
ignoreMissingRoot?: boolean
filter?: (filePath: string, index: number) => boolean
sort?: (current: string, next: string) => number
transformKeys? (keys: string[]) => string[]
}
const options: Partial<Options> = {}
await fsImportAll(location, options)
参数描述
ignoreMissingRoot默认情况下,当根目录缺失时会抛出异常。将 ignoreMissingRoot 设置为 true 不会导致错误,而是会返回空对象。
filter定义一个过滤器来忽略某些路径。默认情况下,只会导入以 .js.ts.json.cjs.mjs 结尾的文件。
sort定义一个自定义方法来对文件路径排序。默认情况下,文件使用自然排序(natural sort)。
transformKeys定义一个回调方法来转换最终对象的键。该方法接收一个嵌套键数组,并且必须返回一个数组。

String builder

StringBuilder 类提供了一个流畅(fluent)的 API 来对字符串值进行转换。你可以使用 string.create 方法获取字符串构建器的实例。

import string from '@adonisjs/core/helpers/string'
const value = string
.create('userController')
.removeSuffix('controller') // user
.plural() // users
.snakeCase() // users
.suffix('_controller') // users_controller
.ext('ts') // users_controller.ts
.toString()

Message builder

MessageBuilder 类提供了一个 API,用于以过期时间和用途(purpose)的方式序列化 JavaScript 数据类型。你可以将序列化后的输出存储在安全的存储中(例如你的应用数据库),或者对其加密(以防止篡改)并公开分享。

在以下示例中,我们序列化一个带有 token 属性的对象,并将其过期时间设置为 1 hour

import { MessageBuilder } from '@adonisjs/core/helpers'
const builder = new MessageBuilder()
const encoded = builder.build(
{
token: string.random(32),
},
'1 hour',
'email_verification'
)
/**
* {
* "message": {
* "token":"GZhbeG5TvgA-7JCg5y4wOBB1qHIRtX6q"
* },
* "purpose":"email_verification",
* "expiryDate":"2022-10-03T04:07:13.860Z"
* }
*/

一旦你得到了带有过期时间和用途的 JSON 字符串,你就可以对其加密(以防止篡改)并与客户端分享。

在令牌验证期间,你可以解密之前加密的值,并使用 MessageBuilder 来验证负载并将其转换为 JavaScript 对象。

import { MessageBuilder } from '@adonisjs/core/helpers'
const builder = new MessageBuilder()
const decoded = builder.verify(value, 'email_verification')
if (!decoded) {
return 'Invalid payload'
}
console.log(decoded.token)

Secret

Secret 类允许你在应用中持有敏感值,而不会将它们意外泄露到日志和控制台语句中。

例如,在 config/app.ts 文件中定义的 appKey 值就是 Secret 类的一个实例。如果你尝试将该值记录到控制台,你会看到 [redacted] 而不是原始值。

为了演示,让我们启动一个 REPL 会话并尝试一下。

node ace repl
> (js) config = await import('./config/app.js')
# [Module: null prototype] {
# appKey: [redacted],
# http: {
# }
# }
> (js) console.log(config.appKey)
# [redacted]

你可以调用 config.appKey.release 方法来读取原始值。Secret 类的目的并非阻止你的代码访问原始值。相反,它提供了一个安全网,避免敏感数据在日志中被暴露。

使用 Secret 类

你可以像下面这样将自定义值包裹在 Secret 类中。

import { Secret } from '@adonisjs/core/helpers'
const value = new Secret('some-secret-value')
console.log(value) // [redacted]
console.log(value.release()) // some-secret-value

Types detection

我们从 helpers/is 导入路径导出 @sindresorhus/is 模块,你可以在应用中使用它来执行类型检测。

import is from '@adonisjs/core/helpers/is'
is.object({}) // true
is.object(null) // false