# 扩展文档服务行为

> Source: https://strapi.nodejs.cn/cms/api/document-service/middlewares

🌐 Document Service API: Middlewares

文档服务中间件允许你在文档服务方法运行之前和之后执行操作，通过 `strapi.documents.use()` 注册中间件函数，并可以访问内容类型上下文和方法参数。

🌐 Document Service middlewares allow you to perform actions before and after Document Service methods run by registering middleware functions via `strapi.documents.use()` with access to content type context and method parameters.

[文档服务 API](/cms/api/document-service) 提供了通过中间件扩展其行为的能力。

🌐 The [Document Service API](/cms/api/document-service) offers the ability to extend its behavior thanks to middlewares.

文档服务中间件允许你在方法运行之前和/或之后执行操作。

🌐 Document Service middlewares allow you to perform actions before and/or after a method runs.

<figure style={{width: '100%', margin: '0'}}>
  <img src="/img/assets/backend-customization/diagram-controllers-services.png" alt="Simplified Strapi backend diagram with controllers highlighted" />
  <em><figcaption style={{fontSize: '12px'}}>该图表示请求在 Strapi 后端传输的简化版本，并突出了文档服务。后端自定义介绍页面包括一个完整的、 <a href="/cms/backend-customization#interactive-diagram">交互式图表</a>。</figcaption></em>
</figure>

## 注册中间件 {#registering-a-middleware}

🌐 Registering a middleware

语法：`strapi.documents.use(middleware)`

🌐 Syntax: `strapi.documents.use(middleware)`

### 参数 {#parameters}

🌐 Parameters

中间件是一种接收上下文和下一个函数的函数。

🌐 A middleware is a function that receives a context and a next function.

语法：`(context, next) => ReturnType<typeof next>`

🌐 Syntax: `(context, next) => ReturnType<typeof next>`

| 参数 | 描述 | 类型 |
|-----------|---------------------------------------|------------|
| `context` | 中间件上下文 | `Context` |
| `next` | 调用堆栈中的下一个中间件 | `function` |

#### `context`

| 参数 | 描述 | 类型 |
|---------------|--------------------------------------------------------------------------------------|---------------|
| `action` | 正在运行的方法（[查看可用方法](/cms/api/document-service)） | `string` |
| `params` | 方法参数（[查看可用方法](/cms/api/document-service)） | `Object` |
| `uid` | 内容类型唯一标识 | `string` |
| `contentType` | 内容类型 | `ContentType` |

<details>
<summary>示例:</summary>

以下示例显示了根据调用的方法，`context` 可能包含的内容：

🌐 The following examples show what `context` might include depending on the method called:

```js
{
  uid: "api::restaurant.restaurant",
  contentType: {
    kind: "collectionType",
    collectionName: "restaurants",
    info: {
      singularName: "restaurant",
      pluralName: "restaurants",
      displayName: "restaurant"
    },
    options: {
      draftAndPublish: true
    },
    pluginOptions: {},
    attributes: {
      name: { /*...*/ },
      description: { /*...*/ },
      createdAt: { /*...*/ },
      updatedAt: { /*...*/ },
      publishedAt: { /*...*/ },
      createdBy: { /*...*/ },
      updatedBy: { /*...*/ },
      locale: { /*...*/ },
    },
    apiName: "restaurant",
    globalId: "Restaurants",
    uid: "api::restaurant.restaurant",
    modelType: "contentType",
    modelName: "restaurant",
    actions: { /*...*/ },
    lifecycles: { /*...*/ },
  },
  action: "findOne",
  params: {
    documentId: 'hp7hjvrbt8rcgkmabntu0aoq',
    locale: undefined,
    status: "publish"
    populate: { /*...*/ },
  }
}
```

```js
{
  uid: "api::restaurant.restaurant",
  contentType: {
    kind: "collectionType",
    collectionName: "restaurants",
    info: {
      singularName: "restaurant",
      pluralName: "restaurants",
      displayName: "restaurant"
    },
    options: {
      draftAndPublish: true
    },
    pluginOptions: {},
    attributes: {
      name: { /*...*/ },
      description: { /*...*/ },
      createdAt: { /*...*/ },
      updatedAt: { /*...*/ },
      publishedAt: { /*...*/ },
      createdBy: { /*...*/ },
      updatedBy: { /*...*/ },
      locale: { /*...*/ },
    },
    apiName: "restaurant",
    globalId: "Restaurants",
    uid: "api::restaurant.restaurant",
    modelType: "contentType",
    modelName: "restaurant",
    actions: { /*...*/ },
    lifecycles: { /*...*/ },
  },
  action: "findMany",
  params: {
    filters: { /*...*/ },
    status: "draft",
    locale: null,
    fields: ['name', 'description'],
  }
}
```

```js
{
  uid: "api::restaurant.restaurant",
  contentType: {
    kind: "collectionType",
    collectionName: "restaurants",
    info: {
      singularName: "restaurant",
      pluralName: "restaurants",
      displayName: "restaurant"
    },
    options: {
      draftAndPublish: true
    },
    pluginOptions: {},
    attributes: {
      name: { /*...*/ },
      description: { /*...*/ },
      createdAt: { /*...*/ },
      updatedAt: { /*...*/ },
      publishedAt: { /*...*/ },
      createdBy: { /*...*/ },
      updatedBy: { /*...*/ },
      locale: { /*...*/ },
    },
    apiName: "restaurant",
    globalId: "Restaurants",
    uid: "api::restaurant.restaurant",
    modelType: "contentType",
    modelName: "restaurant",
    actions: { /*...*/ },
    lifecycles: { /*...*/ },
  },
  action: "create",
  params: {
    data: { /*...*/ },
    status: "draft",
    populate: { /*...*/ },
  }
}
```

```js
{
  uid: "api::restaurant.restaurant",
  contentType: {
    kind: "collectionType",
    collectionName: "restaurants",
    info: {
      singularName: "restaurant",
      pluralName: "restaurants",
      displayName: "restaurant"
    },
    options: {
      draftAndPublish: true
    },
    pluginOptions: {},
    attributes: {
      name: { /*...*/ },
      description: { /*...*/ },
      createdAt: { /*...*/ },
      updatedAt: { /*...*/ },
      publishedAt: { /*...*/ },
      createdBy: { /*...*/ },
      updatedBy: { /*...*/ },
      locale: { /*...*/ },
    },
    apiName: "restaurant",
    globalId: "Restaurants",
    uid: "api::restaurant.restaurant",
    modelType: "contentType",
    modelName: "restaurant",
    actions: { /*...*/ },
    lifecycles: { /*...*/ },
  },
  action: "update",
  params: {
    data: { /*...*/ },
    documentId: 'hp7hjvrbt8rcgkmabntu0aoq',
    locale: undefined,
    status: "draft"
    populate: { /*...*/ },
  }
}
```

```js
{
  uid: "api::restaurant.restaurant",
  contentType: {
    kind: "collectionType",
    collectionName: "restaurants",
    info: {
      singularName: "restaurant",
      pluralName: "restaurants",
      displayName: "restaurant"
    },
    options: {
      draftAndPublish: true
    },
    pluginOptions: {},
    attributes: {
      name: { /*...*/ },
      description: { /*...*/ },
      createdAt: { /*...*/ },
      updatedAt: { /*...*/ },
      publishedAt: { /*...*/ },
      createdBy: { /*...*/ },
      updatedBy: { /*...*/ },
      locale: { /*...*/ },
    },
    apiName: "restaurant",
    globalId: "Restaurants",
    uid: "api::restaurant.restaurant",
    modelType: "contentType",
    modelName: "restaurant",
    actions: { /*...*/ },
    lifecycles: { /*...*/ },
  },
  action: "delete",
  params: {
    data: { /*...*/ },
    documentId: 'hp7hjvrbt8rcgkmabntu0aoq',
    locale: "*",
    populate: { /*...*/ },
  }
}
```
</details>

#### `next`

`next` 是一个没有参数的函数，它调用堆栈中的下一个中间件并返回其响应。

**示例**

```js
strapi.documents.use((context, next) => {
  return next();
});
```

### 在哪里注册 {#where-to-register}

🌐 Where to register

一般来说，你应该在 Strapi 注册阶段注册你的中间件。

🌐 Generaly speaking you should register your middlewares during the Strapi registration phase.

#### 用户 {#users}

🌐 Users

中间件必须在通用的 `register()` 生命周期方法中注册：

🌐 The middleware must be registered in the general `register()` lifecycle method:

```js title="/src/index.js|ts"
module.exports = {
  register({ strapi }) {
    strapi.documents.use((context, next) => {
      // your logic
      return next();
    });
  },

  // bootstrap({ strapi }) {},
  // destroy({ strapi }) {},
};
```

#### 插件开发者 {#plugin-developers}

🌐 Plugin developers

中间件必须在插件的 `register()` 生命周期方法中注册：

🌐 The middleware must be registered in the plugin's `register()` lifecycle method:

```js title="/(plugin-root-folder)/strapi-server.js|ts"
module.exports = {
  register({ strapi }) {
    strapi.documents.use((context, next) => {
      // your logic
      return next();
    });
  },

  // bootstrap({ strapi }) {},
  // destroy({ strapi }) {},
};
```

## 实现中间件 {#implementing-a-middleware}

🌐 Implementing a middleware

在实现中间件时，总是要返回来自 `next()` 的响应。
如果不这样做，将会导致 Strapi 应用出错。

🌐 When implementing a middleware, always return the response from `next()`.
Failing to do this will break the Strapi application.

### 例子 {#examples}

🌐 Examples

```js
const applyTo = ['api::article.article'];

strapi.documents.use((context, next) => {
  // Only run for certain content types
  if (!applyTo.includes(context.uid)) {
    return next();
  }

  // Only run for certain actions
  if (['create', 'update'].includes(context.action)) {
    context.params.data.fullName = `${context.params.data.firstName} ${context.params.data.lastName}`;
  }

  const result = await next();

  // do something with the result before returning it
  return result
});
```

<br/>

:::strapi Lifecycle hooks

文档服务 API 会根据调用的方法触发各种数据库生命周期钩子。完整参考请参见 [文档服务 API：生命周期钩子](/cms/migration/v4-to-v5/breaking-changes/lifecycle-hooks-document-service#table)。

🌐 The Document Service API triggers various database lifecycle hooks based on which method is called. For a complete reference, see [Document Service API: Lifecycle hooks](/cms/migration/v4-to-v5/breaking-changes/lifecycle-hooks-document-service#table).

:::
