服务器 API:策略和中间件
🌐 Server API: Policies & middlewares
Page summary:
就像 Strapi 核心一样,插件也可以有策略和中间件。插件策略在控制器动作之前运行,并返回
true或false来允许或阻止请求。插件中间件在整个请求/响应周期中按顺序运行,并调用next()以继续。将策略和中间件声明为工厂函数的对象,并在路由中通过插件命名空间的名称引用它们。
策略和中间件是插件服务器中拦截请求的两种机制。策略决定请求是否应该继续。中间件决定它如何被处理。
🌐 Policies and middlewares are the two mechanisms for intercepting requests in a plugin server. Policies decide whether a request should proceed. Middlewares shape how it is processed.
在深入了解本页的概念之前,请确保你已经:
🌐 Before diving deeper into the concepts on this page, please ensure you have:
- 创建了一个 Strapi 插件,
- 已阅读并理解了服务器 API的基础知识
决策指南
🌐 Decision guide
在编写任何代码之前,请使用此表选择正确的机制:
🌐 Use this table to pick the right mechanism before writing any code:
| 需求 | 机制 || --- | --- || 根据用户角色或状态阻止请求 | 策略 || 根据请求内容(body, headers)阻止请求 | 策略 或路由配置中的内联策略 || 当条件不满足时返回 403 | 策略 || 在多个路由间重用相同访问规则 | 命名 策略(已注册并通过名称引用) || 为插件的路由的每个响应添加头信息 | 路由级中间件 || 在整个服务器上记录或跟踪每个请求 | 服务器级中间件 (strapi.server.use()) || 在到达控制器之前修改 ctx.query | 路由级中间件 || 在多个路由间共享逻辑 | 命名 路由级中间件(已注册并通过名称引用) |
政策
🌐 Policies
策略是一个在给定路由的控制器操作之前运行的函数。它接收请求上下文,评估一个条件,并返回 true 以允许请求,或者返回 false(或抛出异常)以通过 403 响应阻止请求。
🌐 A policy is a function that runs before the controller action for a given route. It receives the request context, evaluates a condition, and returns true to allow the request or false (or throws) to block it with a 403 response.
声明
🌐 Declaration
策略以普通函数的形式导出(不是工厂函数)。每个策略接收以下三个参数:
🌐 Policies are exported as plain functions (not factory functions). Each policy receives the following 3 arguments:
policyContext是 Koa 上下文对象的一个封装。使用它可以访问policyContext.state.user、policyContext.request等。config包含在附加策略时传入的每条路由配置(例如,{ name: 'plugin::my-plugin.hasRole', options: { role: 'editor' } },其中config是每策略的选项对象)。{ strapi }提供对 Strapi 实例的访问。
policyContext.state.user 的确切形状取决于认证上下文(例如,管理员面板认证与用户与权限 / 内容 API 认证)。请将角色查找逻辑调整为适应你的项目。
🌐 The exact shape of policyContext.state.user depends on the authentication context (for example, admin panel authentication vs. Users & Permissions / Content API authentication). Adapt the role lookup logic to your project.
- JavaScript
- TypeScript
'use strict';
const hasRole = require('./has-role');
module.exports = {
hasRole,
};
'use strict';
// Allow the request only if the user has the role specified in the route config
// Usage in route: { name: 'plugin::my-plugin.hasRole', options: { role: 'editor' } }
module.exports = (policyContext, config, { strapi }) => {
const { user } = policyContext.state;
const targetRole = config.role;
if (!user || !targetRole) {
return false;
}
// Supports both `user.role` and `user.roles` shapes depending on auth strategy.
const roles = Array.isArray(user.roles)
? user.roles
: user.role
? [user.role]
: [];
return roles.some((role) => {
if (typeof role === 'string') return role === targetRole;
return role?.code === targetRole || role?.name === targetRole;
});
};
import hasRole from './has-role';
export default {
hasRole,
};
import type { Core } from '@strapi/strapi';
type UserRole = { code?: string; name?: string };
// Allow the request only if the user has the role specified in the route config
// Usage in route: { name: 'plugin::my-plugin.hasRole', options: { role: 'editor' } }
export default (
policyContext: Core.PolicyContext,
config: { role?: string },
{ strapi }: { strapi: Core.Strapi }
) => {
const { user } = policyContext.state;
const targetRole = config?.role;
if (!user || !targetRole) {
return false;
}
// Supports both `user.role` and `user.roles` shapes depending on auth strategy.
const userWithRoles = user as { roles?: UserRole[]; role?: UserRole };
const roles: UserRole[] = Array.isArray(userWithRoles.roles)
? userWithRoles.roles
: userWithRoles.role
? [userWithRoles.role]
: [];
return roles.some((role) => role?.code === targetRole || role?.name === targetRole);
};
在路由中的使用
🌐 Usage in routes
一旦声明,可以使用 plugin::my-plugin.policy-name 命名空间从路由中引用插件策略:
🌐 Once declared, reference a plugin policy from a route using the plugin::my-plugin.policy-name namespace:
- JavaScript
- TypeScript
'use strict';
module.exports = [
{
method: 'GET',
path: '/dashboard',
handler: 'dashboard.find',
config: {
policies: ['plugin::my-plugin.isActive'], // simple reference by namespaced name
},
},
{
method: 'DELETE',
path: '/articles/:id',
handler: 'article.delete',
config: {
policies: [{ name: 'plugin::my-plugin.hasRole', options: { role: 'editor' } }], // with per-route config
},
},
{
method: 'GET',
path: '/public',
handler: 'article.findAll',
config: {
policies: [(policyContext, config, { strapi }) => true], // inline policy, no registration needed
},
},
];
import type { Core } from '@strapi/strapi';
export default [
{
method: 'GET' as const,
path: '/dashboard',
handler: 'dashboard.find',
config: {
policies: ['plugin::my-plugin.isActive'], // simple reference by namespaced name
},
},
{
method: 'DELETE' as const,
path: '/articles/:id',
handler: 'article.delete',
config: {
policies: [{ name: 'plugin::my-plugin.hasRole', options: { role: 'editor' } }], // with per-route config
},
},
{
method: 'GET' as const,
path: '/public',
handler: 'article.findAll',
config: {
policies: [(policyContext: Core.PolicyContext, config: unknown, { strapi }: { strapi: Core.Strapi }) => true], // inline policy, no registration needed
},
},
];
返回 false 会导致 Strapi 发送 403 Forbidden 响应。返回空 (undefined) 被视为允许(允许),而不是阻止。始终明确返回 true 或 false。抛出错误会导致 Strapi 发送 500 响应,除非你抛出 Strapi HTTP 错误类(例如 new errors.PolicyError(...)、new errors.ForbiddenError(...) 或 new errors.UnauthorizedError(...))。
🌐 Returning false causes Strapi to send a 403 Forbidden response. Returning nothing (undefined) is treated as permissive (allowed), not as a block. Always return true or false explicitly. Throwing an error causes Strapi to send a 500 response unless you throw a Strapi HTTP error class (e.g., new errors.PolicyError(...), new errors.ForbiddenError(...), or new errors.UnauthorizedError(...)).
有关包括 GraphQL 支持和 policyContext API 在内的完整政策参考,请参阅 Policies。
🌐 For the full policy reference including GraphQL support and the policyContext API, see Policies.
中间件
🌐 Middlewares
中间件是一个 Koa 风格的函数,用于封装请求/响应周期。与 策略(它们是通过/不通过的守卫)不同,中间件可以在请求到达控制器之前读取和修改请求,并在控制器执行后修改响应。
🌐 A middleware is a Koa-style function that wraps the request/response cycle. Unlike policies (which are pass/fail guards), middlewares can read and modify the request before it reaches the controller, and modify the response after the controller has executed.
插件可以通过两种方式导出中间件:
🌐 Plugins can export middlewares in 2 ways:
- 作为路由级中间件,在服务器入口文件的
middlewares导出中声明,并在路由config.middlewares中引用 - 作为服务器级中间件,通过
strapi.server.use()在register()中直接注册到 Strapi HTTP 服务器上