帮助函数

实用工具参考

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 package 中导出;因此,请参阅其文档以了解可用选项。

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 package 中导出。

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 属性导出 additional methods 以注册自定义的不可数、不规则、复数和单数规则。

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

singular

将单词转换为单数形式。该方法从 pluralize package 中导出。

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

isSingular

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

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

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

将字符串值转换为首字母大写形式。

import string from '@adonisjs/core/helpers/string'
string.capitalCase('helloWorld') // 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

将字符串值转换为点式命名法。

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

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

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

以下是一些转换示例。

InputOutput
'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

以下是一些转换示例。

InputOutput
'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

将字符串值转换为帕斯卡命名法。非常适合生成 JavaScript 类名。

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

以下是一些转换示例。

InputOutput
'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

以下是一些转换示例。

InputOutput
'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

将值转换为蛇形命名法。

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

以下是一些转换示例。

InputOutput
'_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

将字符串值转换为标题大小写。

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

以下是一些转换示例。

InputOutput
'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 属性将 and 替换为 or

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

检查两个缓冲区或字符串值是否相同。此方法不会泄露任何时间信息,从而防止 timing attack

在底层,此方法使用 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 类混入。以下是没有 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

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

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定义自定义方法来排序文件路径。默认情况下,文件使用自然排序。
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定义自定义方法来对文件路径进行排序。默认情况下,文件使用自然排序。
transformKeys定义回调方法来转换最终对象的键。该方法接收一个嵌套键数组,并必须返回一个数组。

字符串构建器

StringBuilder 类提供了一个流畅的 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()

消息构建器

MessageBuilder 类提供了一个 API 来序列化带有过期时间和用途的 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 类允许你在应用程序中持有敏感值,而不会意外地在日志和控制台语句中泄露它们。

例如,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

类型检测

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

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