Skip to main content

中间件定制

¥Middlewares customization

🤓 不同类型的中间件

在 Strapi 中,有 2 个中间件概念共存:

¥In Strapi, 2 middleware concepts coexist:

  • 整个 Strapi 服务器应用的全局中间件是 配置并启用。这些中间件可以应用于应用级别或 API 级别。
    当前文档描述了如何实现它们。
    插件还可以添加全局中间件(参见 服务器 API 文档)。

    ¥Global middlewares are configured and enabled for the entire Strapi server application. These middlewares can be applied at the application level or at the API level.
    The present documentation describes how to implement them.
    Plugins can also add global middlewares (see Server API documentation).

  • 路由中间件的范围更有限,并且在路由级别被配置和用作中间件。它们在 路由文档 中进行了描述。

    ¥Route middlewares have a more limited scope and are configured and used as middlewares at the route level. They are described in the routes documentation.

Simplified Strapi backend diagram with global middlewares highlighted
The diagram represents a simplified version of how a request travels through the Strapi back end, with global middlewares highlighted. The backend customization introduction page includes a complete, interactive diagram.

执行

¥Implementation

可以实现新的应用级或 API 级中间件:

¥A new application-level or API-level middleware can be implemented:

使用 REST API 的中间件具有如下功能:

¥Middlewares working with the REST API are functions like the following:

./src/middlewares/my-middleware.js or ./src/api/[api-name]/middlewares/my-middleware.js

module.exports = (config, { strapi })=> {
return (context, next) => {};
};

全局范围的自定义中间件应添加到 中间件配置文件 中,否则 Strapi 将不会加载它们。

¥Globally scoped custom middlewares should be added to the middlewares configuration file or Strapi won't load them.

API 级别和插件中间件可以添加到与其相关的特定路由中,如下所示:

¥API level and plugin middlewares can be added into the specific router that they are relevant to like the following:

./src/api/[api-name]/routes/[collection-name].js or ./src/plugins/[plugin-name]/server/routes/index.js
module.exports = {
routes: [
{
method: "GET",
path: "/[collection-name]",
handler: "[controller].find",
config: {
middlewares: ["[middleware-name]"],
// See the usage section below for middleware naming conventions
},
},
],
};
Example of a custom timer middleware
path: /config/middlewares.js
module.exports = () => {
return async (ctx, next) => {
const start = Date.now();

await next();

const delta = Math.ceil(Date.now() - start);
ctx.set('X-Response-Time', delta + 'ms');
};
};

GraphQL 插件还允许使用不同语法的 实现自定义中间件

¥The GraphQL plugin also allows implementing custom middlewares, with a different syntax.

💡 提示

要了解自定义全局中间件可能的高级用法,请阅读后端自定义示例手册的 middlewares 页。

¥To see a possible advanced usage for custom global middlewares, read the middlewares page of the backend customization examples cookbook.

用法

¥Usage

中间件根据其范围有不同的调用方式:

¥Middlewares are called different ways depending on their scope:

  • global::middleware-name 用于应用级中间件

    ¥use global::middleware-name for application-level middlewares

  • 使用 api::api-name.middleware-name 作为 API 级中间件

    ¥use api::api-name.middleware-name for API-level middlewares

  • 使用 plugin::plugin-name.middleware-name 作为插件中间件

    ¥use plugin::plugin-name.middleware-name for plugin middlewares

💡 提示

要列出所有已注册的中间件,请运行 yarn strapi middlewares:list

¥To list all the registered middlewares, run yarn strapi middlewares:list.

使用 "是所有者政策" 限制内容访问

¥Restricting content access with an "is-owner policy"

通常要求条目的作者是唯一允许编辑或删除该条目的用户。在以前版本的 Strapi 中,这被称为 "是所有者政策"。对于 Strapi v4,实现此行为的推荐方法是使用中间件。

¥It is often required that the author of an entry is the only user allowed to edit or delete the entry. In previous versions of Strapi, this was known as an "is-owner policy". With Strapi v4, the recommended way to achieve this behavior is to use a middleware.

正确的实现很大程度上取决于你的项目的需求和自定义代码,但最基本的实现可以通过以下过程来实现:

¥Proper implementation largely depends on your project's needs and custom code, but the most basic implementation could be achieved with the following procedure:

  1. 在项目文件夹中,通过在终端中运行 yarn strapi generate(或 npm run strapi generate)命令,使用 Strapi CLI 生成器创建中间件。

    ¥From your project's folder, create a middleware with the Strapi CLI generator, by running the yarn strapi generate (or npm run strapi generate) command in the terminal.

  2. 使用键盘箭头从列表中选择 middleware,然后按 Enter。

    ¥Select middleware from the list, using keyboard arrows, and press Enter.

  3. 为中间件命名,例如 isOwner

    ¥Give the middleware a name, for instance isOwner.

  4. 从列表中选择 Add middleware to an existing API

    ¥Choose Add middleware to an existing API from the list.

  5. 选择你希望中间件应用哪个 API。

    ¥Select which API you want the middleware to apply.

  6. /src/api/[your-api-name]/middlewares/isOwner.js 文件中的代码替换为以下内容,将第 22 行中的 api::restaurant.restaurant 替换为与你在步骤 5 中选择的 API 对应的标识符(例如,如果你的 API 名称是 blog-post,则为 api::blog-post.blog-post):

    ¥Replace the code in the /src/api/[your-api-name]/middlewares/isOwner.js file with the following, replacing api::restaurant.restaurant in line 22 with the identifier corresponding to the API you choose at step 5 (e.g., api::blog-post.blog-post if your API name is blog-post):

src/api/blog-post/middlewares/isOwner.js
  "use strict";

/**

* `isOwner` middleware
*/

module.exports = (config, { strapi }) => {
// Add your own logic here.
return async (ctx, next) => {
const user = ctx.state.user;
const entryId = ctx.params.id ? ctx.params.id : undefined;
let entry = {};

/**

* Gets all information about a given entry,

* populating every relations to ensure

* the response includes author-related information
*/
if (entryId) {
entry = await strapi.entityService.findOne(
// replace the next line with your proper content-type identifier
"api::restaurant.restaurant",
entryId,
{ populate: "*" }
);
}

/**

* Compares user id and entry author id

* to decide whether the request can be fulfilled

* by going forward in the Strapi backend server
*/
if (user.id !== entry.author.id) {
return ctx.unauthorized("This action is unauthorized.");
} else {
return next();
}
};
};
  1. 确保中间件配置为应用于某些路由。在 src/api/[your-api–name]/routes/[your-content-type-name].js 文件中找到的 config 对象中,定义你希望应用中间件的方法(createreadupdatedelete),并为这些路由声明 isOwner 中间件。

    对于 例如,如果你希望允许对 restaurant API 中的 restaurant 内容类型的任何用户进行 GET(即 read 方法)和 POST(即 create 方法)请求,但希望限制 PUT(即 update 方法)和 仅向创建该条目的用户发出 DELETE 请求,你可以在 src/api/restaurant/routes/restaurant.js 文件中使用以下代码:

    ¥Ensure the middleware is configured to apply on some routes. In the config object found in the src/api/[your-api–name]/routes/[your-content-type-name].js file, define the methods (create, read, update, delete) for which you would like the middleware to apply, and declare the isOwner middleware for these routes.

    For instance, if you wish to allow GET (i.e., read method) and POST (i.e., create method) requests to any user for the restaurant content-type in the restaurant API, but would like to restrict PUT (i.e., update method) and DELETE requests only to the user who created the entry, you could use the following code in the src/api/restaurant/routes/restaurant.js file:

    src/api/restaurant/routes/restaurant.js

    /**

    * restaurant router
    */

    const { createCoreRouter } = require("@strapi/strapi").factories;

    module.exports = createCoreRouter("api::restaurant.restaurant", {
    config: {
    update: {
    middlewares: ["api::restaurant.is-owner"],
    },
    delete: {
    middlewares: ["api::restaurant.is-owner"],
    },
    },
    });
👀 信息

你可以在 路由文档.h 中找到有关路由中间件的更多信息。

¥You can find more information about route middlewares in the routes documentation.