# strapi-utils

> Source: https://strapi.nodejs.cn/cms/strapi-utils

`@strapi/utils` 包提供了用于 Strapi 核心的共享辅助函数，也可用于自定义代码。它包括错误类、环境变量辅助、钩子工厂、类型解析、字符串和文件工具以及异步辅助。

`@strapi/utils` 包（`import { ... } from '@strapi/utils'`）包含 Strapi 内部使用的实用函数，但你也可以在自己的 [controllers](/cms/backend-customization/controllers)、[services](/cms/backend-customization/services)、[policies](/cms/backend-customization/policies)、[middlewares](/cms/backend-customization/middlewares) 和 [lifecycle hooks](/cms/backend-customization/models#lifecycle-hooks) 中使用这些函数。

🌐 The `@strapi/utils` package (`import { ... } from '@strapi/utils'`) contains utility functions that Strapi uses internally but that you can also use in your own [controllers](/cms/backend-customization/controllers), [services](/cms/backend-customization/services), [policies](/cms/backend-customization/policies), [middlewares](/cms/backend-customization/middlewares), and [lifecycle hooks](/cms/backend-customization/models#lifecycle-hooks).

:::tip Finding what you need

本页面的各部分按**导出名称的字母顺序**组织。使用右侧的目录可直接跳转到所需的工具。

🌐 Sections on this page are organized **alphabetically by export name**. Use the table of contents on the right to jump directly to the utility you need.

:::

:::note

本页面的[错误类别](#errors)部分扩展了专门的[错误处理](/cms/error-handling)页面中的错误处理文档。

🌐 The [error classes](#errors) section of this page expands on the error handling documentation found in the dedicated [Error handling](/cms/error-handling) page.

:::

## async

`async` 命名空间提供异步实用函数。它的导入方式如下:

🌐 The `async` namespace provides asynchronous utility functions. It is imported as follows:

```js
const { async } = require('@strapi/utils');
```

以下功能可用：

🌐 The following functions are available:

| 功能 | 描述 |
| --- | --- |
| `async.map(iterable, mapper, options?)` | 使用 `p-map` 的并行映射。通过选项中的 `concurrency` 设置并行度。 |
| `async.pipe(...fns)` | 组合函数：第一个函数使用原始参数运行，每个后续函数接收上一个函数的返回值。返回一个 `Promise`。 |
| `async.reduce(array)(iteratee, initialValue?)` | 对数组进行异步归约。分两步调用：先传入数组，然后传入迭代函数和可选的初始值。迭代函数接收 `(accumulator, item, index)`。 |

以下示例使用 `pipe` 来组合异步函数，并使用 `reduce` 来累积值：

🌐 The following example uses `pipe` to compose async functions, and `reduce` to accumulate values:

```js
const { async: asyncUtils } = require('@strapi/utils');

// Compose async functions into a pipeline
const result = await asyncUtils.pipe(
  fetchUser,
  enrichWithProfile,
  formatResponse
)(userId);

// Reduce an array asynchronously (note the curried call)
const total = await asyncUtils.reduce([1, 2, 3])(
  async (sum, n) => sum + n,
  0
); // 6
```

## contentTypes

`contentTypes` 命名空间公开了用于处理 Strapi 内容类型模式的常量和辅助函数。它的导入方式如下:

🌐 The `contentTypes` namespace exposes constants and helper functions for working with Strapi content-type schemas. It is imported as follows:

```js
const { contentTypes } = require('@strapi/utils');
```

### 常量 {#constants}

🌐 Constants

以下常量可用：

🌐 The following constants are available:

| 常量 | 值 | 描述 |
| --- | --- | --- |
| `ID_ATTRIBUTE` | `'id'` | 主键字段名 |
| `DOC_ID_ATTRIBUTE` | `'documentId'` | 文档标识字段名 |
| `PUBLISHED_AT_ATTRIBUTE` | `'publishedAt'` | 发布时间戳字段名 |
| `FIRST_PUBLISHED_AT_ATTRIBUTE` | `'firstPublishedAt'` | 首次发布时间戳字段名 |
| `CREATED_BY_ATTRIBUTE` | `'createdBy'` | 创建者引用字段名 |
| `UPDATED_BY_ATTRIBUTE` | `'updatedBy'` | 最后编辑者引用字段名 |
| `CREATED_AT_ATTRIBUTE` | `'createdAt'` | 创建时间戳字段名 |
| `UPDATED_AT_ATTRIBUTE` | `'updatedAt'` | 更新时间戳字段名 |
| `SINGLE_TYPE` | `'singleType'` | 单类型标识符 |
| `COLLECTION_TYPE` | `'collectionType'` | 集合类型标识符 |

### 属性检查功能 {#attribute-inspection-functions}

🌐 Attribute inspection functions

以下函数用于检查单个属性的类型：

🌐 The following functions check the type of a single attribute:

| 功能 | 描述 |
| --- | --- |
| `isComponentAttribute(attribute)` | 检查属性是否为组件或动态区域（两者都返回 `true`；使用 `isDynamicZoneAttribute` 进行区分） |
| `isDynamicZoneAttribute(attribute)` | 检查属性是否为动态区域 |
| `isMediaAttribute(attribute)` | 检查属性是否为媒体字段 |
| `isMorphToRelationalAttribute(attribute)` | 检查属性是否为多态关联 |
| `isRelationalAttribute(attribute)` | 检查属性是否为关联 |
| `isScalarAttribute(attribute)` | 检查属性是否为**标量值** 标量值是单个、独立的值，而不是集合或结构。在 Strapi 中，这意味着基本字段，如字符串、数字、布尔值或日期——而不是关联、组件或嵌套对象。 |
| `isTypedAttribute(attribute, type)` | 检查属性是否具有特定类型 |

### 模式检查函数 {#schema-inspection-functions}

🌐 Schema inspection functions

以下函数会检查整个内容类型模式：

🌐 The following functions inspect an entire content-type schema:

| 功能 | 描述 |
| --- | --- |
| `getCreatorFields(schema)` | 返回模式中存在的创建者字段（`createdBy`、`updatedBy`） |
| `getNonWritableAttributes(schema)` | 返回无法写入的字段名称 |
| `getScalarAttributes(schema)` | 返回标量值的属性 |
| `getTimestamps(schema)` | 返回模式中存在的时间戳字段（`createdAt`、`updatedAt`） |
| `getVisibleAttributes(schema)` | 返回未标记为不可见的模式属性 |
| `getWritableAttributes(schema)` | 返回可以写入的字段名称 |
| `hasDraftAndPublish(schema)` | 检查模式是否启用了草稿和发布 |
| `isWritableAttribute(schema, attributeName)` | 检查特定属性是否可写 |

以下示例遍历内容类型的属性以查找关系和可写字段：

🌐 The following example iterates over a content type's attributes to find relations and writable fields:

```js
const { contentTypes } = require('@strapi/utils');

const articleSchema = strapi.contentType('api::article.article');

// List all relation fields
for (const [name, attribute] of Object.entries(articleSchema.attributes)) {
  if (contentTypes.isRelationalAttribute(attribute)) {
    console.log(`${name} is a relation`);
  }
}

// Get only the fields that can be written to
const writableFields = contentTypes.getWritableAttributes(articleSchema);

// Check if draft and publish is enabled
if (contentTypes.hasDraftAndPublish(articleSchema)) {
  console.log('This content type supports drafts');
}
```

## env

一个用于读取环境变量并进行类型安全解析的辅助函数。`env` 函数返回原始字符串值，而它的方法则将该值解析为特定类型。其导入方式如下：

🌐 A helper function to read environment variables with type-safe parsing. The `env` function returns the raw string value, while its methods parse the value to a specific type. It is imported as follows:

```js
const { env } = require('@strapi/utils');
// or in TypeScript: import { env } from '@strapi/utils';
```

`env` 辅助工具可以直接调用，也可以使用以下类型化方法调用：

🌐 The `env` helper can be called directly or with the following typed methods:

| 方法 | 返回类型 | 描述 |
| --- | --- | --- |
| `env(key)` | `string \| undefined` | 返回原始值 |
| `env(key, default)` | `string` | 返回原始值或默认值 |
| `env.array(key, default?)` | `string[] \| undefined` | 按逗号拆分，修剪值，去掉周围的 `[]` 和双引号 |
| `env.bool(key, default?)` | `boolean \| undefined` | `'true'` 返回 `true`，其他任何返回 `false` |
| `env.date(key, default?)` | `Date \| undefined` | 使用 `new Date()` 解析 |
| `env.float(key, default?)` | `number \| undefined` | 解析为浮点数 (`parseFloat`) |
| `env.int(key, default?)` | `number \| undefined` | 解析为整数 (`parseInt`) |
| `env.json(key, default?)` | `object \| undefined` | 解析为 JSON；在 JSON 无效时抛出带有描述性信息的 `Error` |
| `env.oneOf(key, expectedValues, default?)` | `string \| undefined` | 仅当值匹配 `expectedValues` 中的某个值时才返回，否则返回 `default`。如果未提供 `expectedValues` 或 `default` 本身不在 `expectedValues` 中时将抛出异常。 |

以下示例显示了如何在服务器配置文件中使用 `env` 辅助工具：

🌐 The following example shows how to use `env` helpers in a server configuration file:

```js title="/config/server.js"
const { env } = require('@strapi/utils');

module.exports = {
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  app: {
    keys: env.array('APP_KEYS'),
  },
};
```

## errors

继承 Node.js `Error` 类的自定义错误类。所有错误都共享一个通用结构：

🌐 Custom error classes that extend the Node.js `Error` class. All errors share a common structure:

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| `name` | `string` | 错误类名（例如，`'ApplicationError'`、`'ValidationError'`） |
| `message` | `string` | 可读的错误信息 |
| `details` | `object` | 额外的错误上下文 |

错误类导入如下：

🌐 The error classes are imported as follows:

```js
const { errors } = require('@strapi/utils');
// or in TypeScript: import { errors } from '@strapi/utils';
```

以下错误类可用：

🌐 The following error classes are available:

| 错误类别 | 默认消息 | 详细信息默认值 |
| --- | --- | --- |
| `ApplicationError` | `'An application error occurred'` | `{}` |
| `ValidationError` | （必填） | 取决于构造函数输入 |
| `YupValidationError` | `'Validation'`（或格式化的 Yup 消息） | `{ errors: [] }` |
| `PaginationError` | `'Invalid pagination'` | 取决于构造函数输入 |
| `NotFoundError` | `'Entity not found'` | 取决于构造函数输入 |
| `ForbiddenError` | `'Forbidden access'` | 取决于构造函数输入 |
| `UnauthorizedError` | `'Unauthorized'` | 取决于构造函数输入 |
| `RateLimitError` | `'Too many requests, please try again later.'` | `{}` |
| `PayloadTooLargeError` | `'Entity too large'` | 取决于构造函数输入 |
| `PolicyError` | `'Policy Failed'` | `{}` |
| `NotImplementedError` | `'This feature is not implemented yet'` | 取决于构造函数输入 |

`PolicyError` 继承自 `ForbiddenError`。其他所有错误类都继承自 `ApplicationError`。

以下示例展示了如何在服务和策略中抛出错误：

🌐 The following example shows how to throw errors in a service and a policy:

```js
const { errors } = require('@strapi/utils');

// In a service or lifecycle hook
throw new errors.ApplicationError('Something went wrong', { foo: 'bar' });

// In a policy
throw new errors.PolicyError('Access denied', { policy: 'is-owner' });
```

:::tip

在模型生命周期钩子中抛出错误时使用 `ApplicationError`，以便在管理面板中显示有意义的消息。有关更多示例，请参阅 [错误处理](/cms/error-handling) 页面。

🌐 Use `ApplicationError` when throwing errors in model lifecycle hooks so that meaningful messages display in the admin panel. See the [Error handling](/cms/error-handling) page for more examples.

:::

## file

`file` 命名空间提供了用于处理流和文件大小的辅助工具。它的导入方式如下:

🌐 The `file` namespace provides helpers for working with streams and file sizes. It is imported as follows:

```js
const { file } = require('@strapi/utils');
```

以下功能可用：

🌐 The following functions are available:

| 函数 | 返回类型 | 描述 |
| --- | --- | --- |
| `bytesToHumanReadable(bytes)` | `string` | 将字节格式化为人类可读的字符串（例如，`'2 MB'`） |
| `bytesToKbytes(bytes)` | `number` | 将字节转换为千字节（保留两位小数） |
| `getStreamSize(stream)` | `Promise<number>` | 计算流的总字节数 |
| `kbytesToBytes(kbytes)` | `number` | 将千字节转换为字节 |
| `streamToBuffer(stream)` | `Promise<Buffer>` | 将可读流转换为 Buffer |
| `writableDiscardStream(options?)` | `Writable` | 创建一个会丢弃所有数据的可写流 |

以下示例将上传的流转换为缓冲区并记录其大小：

🌐 The following example converts an uploaded stream to a buffer and logs its size:

```js
const { file } = require('@strapi/utils');

const buffer = await file.streamToBuffer(uploadStream);
const sizeInKb = file.bytesToKbytes(buffer.length);
console.log(`Uploaded ${file.bytesToHumanReadable(buffer.length)} (${sizeInKb} KB)`);
```

## hooks

用于创建钩子注册表的工厂函数。钩子让你可以注册处理函数并以不同的模式执行它们。命名空间的导入方式如下：

🌐 Factory functions to create hook registries. Hooks let you register handler functions and execute them in different patterns. The namespace is imported as follows:

```js
const { hooks } = require('@strapi/utils');
```

每个 hook 实例提供以下 4 个方法：

🌐 Each hook instance exposes the following 4 methods:

| 方法 | 描述 |
| --- | --- |
| `register(handler)` | 向钩子添加处理函数 |
| `delete(handler)` | 移除先前注册的处理函数 |
| `getHandlers()` | 返回已注册处理函数的列表 |
| `call(...args)` | 根据钩子类型执行已注册的处理函数 |

### 可用的钩子工厂 {#available-hook-factories}

🌐 Available hook factories

以下工厂函数创建不同的钩子类型。当处理程序必须按顺序运行时使用 **series**，当每个处理程序为下一个处理程序转换数据时使用 **waterfall**，当处理程序独立且可以同时运行时使用 **parallel**，当你需要第一个返回值的处理程序中止其余处理程序时使用 **bail**：

🌐 The following factory functions create different hook types. Use **series** when handlers must run in order, **waterfall** when each handler transforms data for the next, **parallel** when handlers are independent and can run concurrently, and **bail** when you need the first handler that returns a value to short-circuit the rest:

| 工厂 | 执行模式 |
| --- | --- |
| `hooks.createAsyncSeriesHook()` | 使用相同的上下文依次执行处理器 |
| `hooks.createAsyncSeriesWaterfallHook()` | 依次执行处理器，将每个返回值传递给下一个处理器 |
| `hooks.createAsyncParallelHook()` | 并发执行所有处理器 |
| `hooks.createAsyncBailHook()` | 依次执行处理器，遇到第一个返回非 `undefined` 值的处理器时停止 |

下面的例子注册并调用带有 **series hook** 的处理程序 series hook 按顺序依次执行处理程序，按照它们注册的顺序一个接一个地运行。其他模式包括 waterfall（每个处理程序接收前一个处理程序的返回值）、parallel（所有处理程序并发运行）以及 bail（在第一个返回值的处理程序处停止）。更多详情请参见 [admin hooks](/cms/plugins-development/admin-hooks):

```js
const { hooks } = require('@strapi/utils');

const myHook = hooks.createAsyncSeriesHook();

myHook.register(async (context) => {
  console.log('First handler', context);
});

myHook.register(async (context) => {
  console.log('Second handler', context);
});

// Execute all handlers in order
await myHook.call({ data: 'example' });
```

## pagination

`pagination` 命名空间提供用于处理分页参数的辅助工具。导入方法如下:

🌐 The `pagination` namespace provides helpers for handling pagination parameters. It is imported as follows:

```js
const { pagination } = require('@strapi/utils');
```

以下功能可用：

🌐 The following functions are available:

| 功能 | 描述 |
| --- | --- |
| `transformOffsetPaginationInfo(params, total)` | 将分页数据转换为 `{ start, limit, total }` 格式 |
| `transformPagedPaginationInfo(params, total)` | 将分页数据转换为 `{ page, pageSize, pageCount, total }` 格式 |
| `withDefaultPagination(params, options?)` | 应用默认值并验证分页参数（详见下文） |

`withDefaultPagination` 函数同时支持 `page`/`pageSize` 和 `start`/`limit` 格式。它可以接受一个可选的 `options` 对象，该对象具有以下属性：

🌐 The `withDefaultPagination` function supports both `page`/`pageSize` and `start`/`limit` formats. It accepts an optional `options` object with the following properties:

| 选项 | 类型 | 描述 |
| --- | --- | --- |
| `defaults` | `object` | 覆盖每种格式的初始分页值（例如，`{ page: { pageSize: 25 } }`） |
| `maxLimit` | `number` | 限制 `limit` 或 `pageSize` 值的上限。设置为 `-1` 表示无限制。 |

以下示例应用默认分页并转换结果：

🌐 The following example applies default pagination and transforms the result:

```js
const { pagination } = require('@strapi/utils');

const params = pagination.withDefaultPagination({ page: 2 }, { maxLimit: 100 });
const info = pagination.transformPagedPaginationInfo(params, 250);
// { page: 2, pageSize: 25, pageCount: 10, total: 250 }
```

## parseType

将一个值转换为特定的 Strapi 字段类型。函数导入如下：

🌐 Cast a value to a specific Strapi field type. The function is imported as follows:

```js
const { parseType } = require('@strapi/utils');
```

该函数接受以下参数：

🌐 The function accepts the following parameters:

| 参数 | 类型 | 描述 |
| --- | --- | --- |
| `type` | `string` | 目标类型：`'boolean'`、`'integer'`、`'biginteger'`、`'float'`、`'decimal'`、`'time'`、`'date'`、`'timestamp'` 或 `'datetime'` |
| `value` | `unknown` | 要解析的值 |
| `forceCast` | `boolean` | 强制布尔值转换。默认值：`false` |

返回值取决于目标类型：

🌐 The return value depends on the target type:

| 类型 | 返回类型 | 格式 |
| --- | --- | --- |
| `boolean` | `boolean` | 接受 `'true'`、`'t'`、`'1'`、`1` 作为 `true` |
| `integer`、`biginteger`、`float`、`decimal` | `number` | 数值转换 |
| `time` | `string` | `HH:mm:ss.SSS` |
| `date` | `string` | `yyyy-MM-dd` |
| `timestamp`、`datetime` | `Date` | 日期对象 |

以下示例演示了解析不同字段类型：

🌐 The following example demonstrates parsing different field types:

```js
parseType({ type: 'boolean', value: 'true' }); // true
parseType({ type: 'integer', value: '42' }); // 42
parseType({ type: 'date', value: '2024-01-15T10:30:00Z' }); // '2024-01-15'
```

## policy

用于创建和管理[策略](/cms/backend-customization/policies)的辅助工具。该命名空间提供两个函数：[``createPolicy``](#createpolicy)用于定义带有可选配置验证器的策略处理程序，和[``createPolicyContext``](#createpolicycontext)用于构建处理程序可以检查的类型化上下文对象。该命名空间的导入方式如下：

🌐 Helpers to create and manage [policies](/cms/backend-customization/policies). The namespace exposes 2 functions: [`createPolicy`](#createpolicy) to define a policy handler with an optional configuration validator, and [`createPolicyContext`](#createpolicycontext) to build a typed context object that the handler can inspect. The namespace is imported as follows:

```js
const { policy } = require('@strapi/utils');
```

### createPolicy

创建一个带有可选配置验证器的策略。该函数接受以下参数：

🌐 Create a policy with an optional configuration validator. The function accepts the following parameters:

| 参数 | 类型 | 必填 | 描述 |
| --- | --- | --- | --- |
| `name` | `string` | 否 | 策略名称（默认值为 `'unnamed'`） |
| `handler` | `function` | 是 | 策略处理函数 |
| `validator` | `function` | 否 | 验证策略配置；配置无效时抛出错误 |

以下示例创建了一个带有配置验证器的策略：

🌐 The following example creates a policy with a configuration validator:

```js
const myPolicy = policy.createPolicy({
  name: 'is-owner',
  validator: (config) => {
    if (!config.field) throw new Error('Missing field');
  },
  handler: (ctx, config, { strapi }) => {
    // policy logic
    return true;
  },
});
```

### createPolicyContext

`createPolicyContext` 函数为策略处理程序创建一个类型化的上下文对象。它接受一个类型字符串（例如，`'admin'` 或 `'koa'`）和 Koa 上下文，并返回一个具有 `is()` 方法和 `type` 属性的对象：

🌐 The `createPolicyContext` function creates a typed context object for use within a policy handler. It accepts a type string (e.g., `'admin'` or `'koa'`) and the Koa context, and returns an object with an `is()` method and a `type` property:

```js
const policyCtx = policy.createPolicyContext('admin', ctx);

policyCtx.is('admin'); // true
policyCtx.type; // 'admin'
```

## primitives

低级数据转换辅助工具。以下子模块可以作为来自 `@strapi/utils` 的直接顶层导入使用：

🌐 Low-level data transformation helpers. The following sub-modules are available as direct top-level imports from `@strapi/utils`:

```js
const { strings, objects, arrays, dates } = require('@strapi/utils');
```

### strings

以下字符串实用函数可用：

🌐 The following string utility functions are available:

| 功能 | 描述 |
| --- | --- |
| `strings.getCommonPath(...paths)` | 从多个文件路径中找到公共路径前缀 |
| `strings.isCamelCase(value)` | 检查字符串是否为 `camelCase` 格式 |
| `strings.isEqual(a, b)` | 将两个值作为字符串进行比较 |
| `strings.isKebabCase(value)` | 检查字符串是否为 `kebab-case` 格式 |
| `strings.joinBy(separator, ...parts)` | 使用分隔符连接字符串，并在连接点去除重复的分隔符 |
| `strings.nameToCollectionName(name)` | 将名称转换为 `snake_case` 集合名称 |
| `strings.nameToSlug(name, options?)` | 将名称转换为适合 URL 的短标签。默认分隔符：`'-'` |
| `strings.startsWithANumber(value)` | 检查字符串是否以数字开头 |
| `strings.toKebabCase(value)` | 将字符串转换为 `kebab-case` |
| `strings.toRegressedEnumValue(value)` | 将带音符的字符替换为其 ASCII 等效字符，然后用下划线分隔单词，以生成适合作为枚举键使用的字符串（保留原始大小写） |

### objects

以下对象实用函数可用：

🌐 The following object utility function is available:

| 功能 | 描述 |
| --- | --- |
| `objects.keysDeep(obj)` | 返回所有嵌套键的点表示法（例如，`['a.b', 'a.c']`） |

### arrays

以下数组工具函数可用：

🌐 The following array utility function is available:

| 功能 | 描述 |
| --- | --- |
| `arrays.includesString(arr, val)` | 当数组和要检查的值都作为字符串比较时，检查数组是否包含该值 |

### dates

以下日期工具函数可用：

🌐 The following date utility function is available:

| 功能 | 描述 |
| --- | --- |
| `dates.timestampCode(date?)` | 将 `Date`（默认为 `new Date()`）转换为毫秒时间戳的 36 进制字符串 |

## providerFactory

创建一个插件化的注册表，通过键存储和检索项目，并带有生命周期钩子。这与 Strapi 内部用于其上传和邮件提供者的工厂相同。当你在自己的插件中需要一个可互换策略或适配器的存储时，使用 `providerFactory`。该工厂的导入方式如下：

🌐 Create a pluggable registry that stores and retrieves items by key, with lifecycle hooks. This is the same factory Strapi uses internally for its upload and email providers. Use `providerFactory` when you need a store of interchangeable strategies or adapters in your own plugins. The factory is imported as follows:

```js
const { providerFactory } = require('@strapi/utils');
```

### 参数 {#parameters}

🌐 Parameters

工厂接受以下参数：

🌐 The factory accepts the following parameter:

| 参数 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| `throwOnDuplicates` | `boolean` | `true` | 当注册已存在的键时抛出错误 |

### 提供者方法 {#provider-methods}

🌐 Provider methods

返回的提供者实例暴露以下方法：

🌐 The returned provider instance exposes the following methods:

| 方法 | 返回类型 | 描述 |
| --- | --- | --- |
| `register(key, item)` | `Promise<Provider>` | 注册一个项目。触发 `willRegister` 和 `didRegister` [钩子](#provider-hooks)。|
| `delete(key)` | `Promise<Provider>` | 移除一个项目。触发 `willDelete` 和 `didDelete` [钩子](#provider-hooks)。|
| `get(key)` | `T \| undefined` | 根据键检索一个项目 |
| `values()` | `T[]` | 返回所有注册的项目 |
| `keys()` | `string[]` | 返回所有注册的键 |
| `has(key)` | `boolean` | 检查一个键是否已注册 |
| `size()` | `number` | 返回注册项目的数量 |
| `clear()` | `Promise<Provider>` | 移除所有项目 |

### 提供者钩子 {#provider-hooks}

🌐 Provider hooks

每个提供者实例都暴露一个 `hooks` 对象，该对象具有 4 个钩子注册表：

🌐 Each provider instance exposes a `hooks` object with 4 hook registries:

| 钩子 | 类型 | 触发条件 |
| --- | --- | --- |
| `hooks.willRegister` | 异步串行 | 在项目注册之前 |
| `hooks.didRegister` | 异步并行 | 在项目注册之后 |
| `hooks.willDelete` | 异步并行 | 在项目删除之前 |
| `hooks.didDelete` | 异步并行 | 在项目删除之后 |

以下示例创建了一个提供者并注册了一个带有生命周期钩子的项目：

🌐 The following example creates a provider and registers an item with a lifecycle hook:

```js
const { providerFactory } = require('@strapi/utils');

const registry = providerFactory();

registry.hooks.willRegister.register(async ({ key, value }) => {
  console.log(`About to register: ${key}`);
});

await registry.register('my-provider', { execute: () => {} });

registry.get('my-provider'); // { execute: [Function] }
registry.has('my-provider'); // true
registry.size(); // 1
```

## relations

`relations` 命名空间提供了用于检查关系属性基数（例如，一对多与多对多）的辅助工具。要检查一个属性是否是关系，请改用 [`contentTypes.isRelationalAttribute`](#attribute-inspection-functions)。该命名空间的导入方式如下:

🌐 The `relations` namespace provides helpers to inspect the cardinality of relation attributes (e.g., one-to-many vs. many-to-many). To check whether an attribute is a relation at all, use [`contentTypes.isRelationalAttribute`](#attribute-inspection-functions) instead. The namespace is imported as follows:

```js
const { relations } = require('@strapi/utils');
```

以下功能可用：

🌐 The following functions are available:

| 功能 | 描述 |
| --- | --- |
| `getRelationalFields(contentType)` | 返回内容类型中的所有关联字段名称 |
| `isAnyToMany(attribute)` | 检查 `oneToMany` 或 `manyToMany` 关联 |
| `isAnyToOne(attribute)` | 检查 `oneToOne` 或 `manyToOne` 关联 |
| `isManyToAny(attribute)` | 检查 `manyToMany` 或 `manyToOne` 关联 |
| `isOneToAny(attribute)` | 检查 `oneToOne` 或 `oneToMany` 关联 |
| `isPolymorphic(attribute)` | 检查 `morphOne`、`morphMany`、`morphToOne` 或 `morphToMany` 关联 |

以下示例过滤内容类型的属性，以查找所有一对多或多对多的关系：

🌐 The following example filters a content type's attributes to find all one-to-many or many-to-many relations:

```js
const { relations, contentTypes } = require('@strapi/utils');

const schema = strapi.contentType('api::article.article');

for (const [name, attribute] of Object.entries(schema.attributes)) {
  if (contentTypes.isRelationalAttribute(attribute) && relations.isAnyToMany(attribute)) {
    console.log(`${name} is a *-to-many relation`);
  }
}
```

## sanitize

`sanitize` 命名空间提供根据内容类型模式清理输入和输出数据的功能。使用 `sanitize` 在处理或返回数据之前移除不允许、私有或受限制的字段。

🌐 The `sanitize` namespace provides functions to clean input and output data based on content-type schemas. Use `sanitize` to remove disallowed, private, or restricted fields before processing or returning data.

:::tip

在大多数控制器中，你不需要直接调用 `sanitize`。Strapi 提供了内置的 `sanitizeQuery` 和 `sanitizeOutput` 辅助工具来为你处理设置（详情请参见 [Controllers](/cms/backend-customization/controllers#sanitization-and-validation-in-controllers) 文档）。当你需要在控制器上下文之外进行清理时（例如，在服务或自定义脚本中），请使用下面的低级 API。

🌐 In most controllers, you do not need to call `sanitize` directly. Strapi provides built-in `sanitizeQuery` and `sanitizeOutput` helpers that handle the setup for you (see [Controllers](/cms/backend-customization/controllers#sanitization-and-validation-in-controllers) documentation for details). Use the lower-level API below when you need sanitization outside of a controller context (e.g., in a service or a custom script).

:::

命名空间导入如下：

🌐 The namespace is imported as follows:

```js
const { sanitize } = require('@strapi/utils');
```

`createAPISanitizers` 函数接受一个模型解析器，并返回一组针对该模型的消毒方法。模型解析器是一个函数，给定内容类型 UID（例如 `'api::article.article'`），返回对应的模式。实际上，`strapi.getModel` 已经做到这一点。你通常在引导期间或服务顶部调用 `createAPISanitizers` 一次:

🌐 The `createAPISanitizers` function takes a model resolver and returns a set of sanitizer methods scoped to that model. A model resolver is a function that, given a content-type UID (e.g., `'api::article.article'`), returns the corresponding schema. In practice, `strapi.getModel` already does this. You typically call `createAPISanitizers` once during bootstrap or at the top of a service:

```js
const sanitizers = sanitize.createAPISanitizers({
  getModel: strapi.getModel.bind(strapi),
});
```

返回的对象提供以下内容：

🌐 The returned object provides the following:

| 方法 | 描述 |
| --- | --- |
| `sanitizers.input(data, schema, options?)` | 清理请求体数据 |
| `sanitizers.output(data, schema, options?)` | 清理响应数据 |
| `sanitizers.query(query, schema, options?)` | 清理查询参数 |
| `sanitizers.filters(filters, schema, options?)` | 清理过滤表达式 |
| `sanitizers.sort(sort, schema, options?)` | 清理排序参数 |
| `sanitizers.fields(fields, schema, options?)` | 清理字段选择 |
| `sanitizers.populate(populate, schema, options?)` | 清理填充指令 |

每个方法都接受一个可选的 `options` 对象，该对象具有以下属性：

🌐 Each method accepts an optional `options` object with the following properties:

| 选项 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| `auth` | `object` | `undefined` | 来自请求的认证对象（通常为 `ctx.state.auth`）。提供时，用户无权限访问的关联字段将从输出中移除。省略时，不会应用基于权限的过滤。 |
| `strictParams` | `boolean` | `false` | 当为 `true` 时，会移除内容类型模式中未声明的字段或查询参数。当为 `false` 时，未识别的字段会通过。 |
| `route` | `object` | `undefined` | 路由对象（通常为 `ctx.route`）。当 `strictParams` 为 `true` 时，清理器会从路由配置中读取 `request` 键，以确定核心集合之外允许的自定义查询或请求体参数。当 `strictParams` 为 `false` 时无效。有关路由配置的详细信息，请参见 [Routes](/cms/backend-customization/routes)。 |

## setCreatorFields

在一个实体上设置 `createdBy` 和 `updatedBy` 字段。当构建自定义控制器或服务以在 Strapi 默认的 [Document Service](/cms/api/document-service) 之外创建或更新条目时使用。该函数返回一个**柯里化函数** 柯里化函数是一个不会一次接受所有参数的函数。相反，它会为每个参数返回一个新函数。这允许你先固定一些参数，然后稍后传入其余参数。例如，`setCreatorFields({ user })` 返回一个可重用函数，你可以在任何实体数据上调用它。: 先用选项调用它，然后再用实体数据调用。它的导入方式如下:

```js
const { setCreatorFields } = require('@strapi/utils');
```

该函数接受以下参数：

🌐 The function accepts the following parameters:

| 参数 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| `user` | `{ id: string \| number }` | （必填） | 执行操作的用户 |
| `isEdition` | `boolean` | `false` | 如果 `true`，只设置 `updatedBy`；如果 `false`，同时设置 `createdBy` 和 `updatedBy` |

下面的示例展示了如何在创建和更新时设置创建者字段：

🌐 The following example shows how to set creator fields on creation and update:

```js
const { setCreatorFields } = require('@strapi/utils');

const addCreator = setCreatorFields({ user: { id: 1 } });
const data = addCreator({ title: 'My Article' });
// { title: 'My Article', createdBy: 1, updatedBy: 1 }

const updateCreator = setCreatorFields({ user: { id: 2 }, isEdition: true });
const updated = updateCreator(data);
// { title: 'My Article', createdBy: 1, updatedBy: 2 }
```

## validate

`validate` 命名空间提供了用于检查输入和根据内容类型模式查询数据的函数。使用 `validate` 拒绝引用未知的、私有的或受限制字段的请求。

🌐 The `validate` namespace provides functions to check input and query data against content-type schemas. Use `validate` to reject requests that reference unknown, private, or restricted fields.

:::tip

像 `sanitize` 一样，控制器已经提供了内置的验证辅助工具（`validateQuery`、`validateInput`）。当你需要在控制器之外的上下文中进行验证时，请使用下面的低级 API。

🌐 Like `sanitize`, controllers already provide built-in validation helpers (`validateQuery`, `validateInput`). Use the lower-level API below when you need validation outside of a controller context.

:::

命名空间导入如下：

🌐 The namespace is imported as follows:

```js
const { validate } = require('@strapi/utils');
```

`createAPIValidators` 函数接受一个模型解析器（详情请参见 [sanitize](#sanitize)）并返回一组针对该模型的验证方法：

🌐 The `createAPIValidators` function takes a model resolver (see [sanitize](#sanitize) for details) and returns a set of validator methods scoped to that model:

```js
const validators = validate.createAPIValidators({
  getModel: strapi.getModel.bind(strapi),
});
```

返回的对象提供以下内容：

🌐 The returned object provides the following:

| 方法 | 描述 |
| --- | --- |
| `validators.input(data, schema, options?)` | 验证请求主体数据 |
| `validators.query(query, schema, options?)` | 验证查询参数 |
| `validators.filters(filters, schema, options?)` | 验证过滤表达式 |
| `validators.sort(sort, schema, options?)` | 验证排序参数 |
| `validators.fields(fields, schema, options?)` | 验证字段选择 |
| `validators.populate(populate, schema, options?)` | 验证填充指令 |

每个方法都接受一个可选的 `options` 对象，该对象具有与 [sanitize options](#sanitize) 相同的属性：用于基于权限检查的 `auth`、用于拒绝未知字段的 `strictParams`，以及在严格模式下允许自定义路由参数的 `route`。

🌐 Each method accepts an optional `options` object with the same properties as the [sanitize options](#sanitize): `auth` for permission-based checks, `strictParams` to reject unknown fields, and `route` to allow custom route parameters in strict mode.

以下示例验证自定义服务中的查询并捕获错误：

🌐 The following example validates a query in a custom service and catches the error:

```js
const { validate, errors } = require('@strapi/utils');

const validators = validate.createAPIValidators({
  getModel: strapi.getModel.bind(strapi),
});

try {
  await validators.query(ctx.query, 'api::article.article', {
    auth: ctx.state.auth,
  });
} catch (error) {
  // error is a ValidationError with details about which fields failed
  console.error(error.message, error.details);
}
```

## yup

`yup` 命名空间重新导出带有 Strapi 特定扩展的  [Yup validation library](https://github.com/jquense/yup) 。其导入方式如下:

```js
const { yup } = require('@strapi/utils');
```

### 附加 Yup 方法 {#additional-yup-methods}

🌐 Additional Yup methods

Strapi 为 Yup 模式添加了以下方法：

🌐 Strapi adds the following methods to Yup schemas:

| 方法 | 架构类型 | 描述 |
| --- | --- | --- |
| `yup.strapiID()` | 自定义 | 验证 Strapi ID（字符串或非负整数） |
| `.notNil()` | 任意 | 确保值不是 `undefined` 或 `null` |
| `.notNull()` | 任意 | 确保值不是 `null` |
| `.isFunction()` | 混合 | 验证值是否为函数 |
| `.isCamelCase()` | 字符串 | 验证 `camelCase` 格式 |
| `.isKebabCase()` | 字符串 | 验证 `kebab-case` 格式 |
| `.onlyContainsFunctions()` | 对象 | 验证对象中所有值是否为函数 |
| `.uniqueProperty(property, message)` | 数组 | 验证数组项中特定属性是否唯一 |

### 模式验证助手 {#schema-validation-helpers}

🌐 Schema validation helpers

`validateYupSchema` 和 `validateYupSchemaSync` 是来自 `@strapi/utils` 的顶层导出，而不是 `yup` 命名空间的一部分：

```js
const { validateYupSchema, validateYupSchemaSync } = require('@strapi/utils');
```

以下可用的辅助函数有：

🌐 The following helper functions are available:

| 功能 | 描述 |
| --- | --- |
| `validateYupSchema(schema, options?)` | 返回一个用于 Yup 模式的异步验证器函数 `(body, errorMessage?) => Promise`。默认选项：`{ strict: true, abortEarly: false }`。 |
| `validateYupSchemaSync(schema, options?)` | 返回一个用于 Yup 模式的同步验证器函数 `(body, errorMessage?) => result`。默认选项：`{ strict: true, abortEarly: false }`。 |

## zod

Strapi 重新导出来自  [Zod](https://github.com/colinhacks/zod)  的 `z` 实例，并提供一个 `validateZod` 辅助工具，将 Zod 模式封装成 Strapi 风格的验证器。Strapi 不会向 Zod 添加自定义方法。`z` 是标准的 Zod API。辅助工具的导入方式如下：

```js
const { validateZod, z } = require('@strapi/utils');
```

以下示例定义了一个模式，并使用 `validateZod` 创建了一个验证函数。成功时，该函数返回解析后的数据。失败时，它会抛出一个 `ValidationError`（参见 [errors](#errors)），其中包含关于哪些字段验证失败的详细信息：

🌐 The following example defines a schema and creates a validator function with `validateZod`. On success, the function returns the parsed data. On failure, it throws a `ValidationError` (see [errors](#errors)) with details about which fields failed:

```js
const schema = z.object({
  name: z.string().min(1),
  age: z.number().positive(),
});

const validate = validateZod(schema);

const parsed = validate({ name: 'Alice', age: 30 }); // returns parsed data
validate({ name: '' }); // throws ValidationError
```
