# 后端定制

# Routing 路由

./api/**/config/routes.json 文件定义客户机的所有可用端点.

默认情况下,Strapi 为所有的 Content Types 生成端点,更多内容参考 Content API

# 如何创建一条路由?

您必须在您的一个 api 文件夹中编辑 routes.json 文件(./api/**/config/routes.json)并手动向 routes 数组中添加一个新的 route array 对象。

Path — ./api/**/config/routes.json.

{
  "routes": [
    {
      "method": "GET",
      "path": "/restaurants",
      "handler": "Restaurant.find",
      "config": {
        "policies": []
      }
    },
    {
      "method": "PUT",
      "path": "/restaurants/bulkUpdate",
      "handler": "Restaurant.bulkUpdate",
      "config": {
        "policies": []
      }
    },
    {
      "method": "POST",
      "path": "/restaurants/:id/reservation",
      "handler": "Restaurant.reservation",
      "config": {
        "policies": ["isAuthenticated", "hasCreditCard"]
      }
    }
  ]
}
  • method (string): 命中路由的方法的方法或数组 (e.g. GET, POST, PUT, HEAD, DELETE, PATCH).
  • path (string): 启动路径 URL / (e.g. /restaurants).
  • handler (string): 当按照此语法命中路由时执行的操作 <Controller>.<action>.
  • config
    • policies (array): 策略名称或路径的数组 (更多查看)

TIP

如果你不想让 用户&权限插件 检查路由,你可以排除整个 config 对象。

# 动态参数

Strapi 使用的路由器允许您创建可以使用参数和简单正则表达式的动态路由。这些参数将在 ctx.params 对象中公开。有关更多细节,请参考 PathToRegex (opens new window) 文档。

{
  "routes": [
    {
      "method": "GET",
      "path": "/restaurants/:category/:id",
      "handler": "Restaurant.findOneByCategory",
      "config": {
        "policies": []
      }
    },
    {
      "method": "GET",
      "path": "/restaurants/:region(\\d{2}|\\d{3})/:id", // Only match when the first parameter contains 2 or 3 digits.
      "handler": "Restaurant.findOneByRegion",
      "config": {
        "policies": []
      }
    }
  ]
}

# 例子

带有 URL 参数的路由定义

{
  "routes": [
    {
      "method": "GET",
      "path": "/restaurants/:id",
      "handler": "Restaurant.findOne",
      "config": {
        "policies": []
      }
    }
  ]
}

获取控制器中的 URL 参数

module.exports = {
  findOne: async ctx => {
    // const id = ctx.params.id;
    const { id } = ctx.params;
    return id;
  },
};

# Policies 策略

策略是能够在每个请求到达控制器的操作之前对其执行特定逻辑的函数。它们主要用于简单地保护业务逻辑。项目的每个路由都可以关联到一组策略。例如,您可以创建名为 isAdmin 的策略,该策略显然检查请求是否由管理员用户发送,并将其用于关键路由。

策略定义在了 ./api/**/config/policies/ 目录中. 它们分别通过 strapi.api.**.config.policiesstrapi.plugins.**.config.policies 配置策略。全局策略定义在 ./config/policies/ ,可通过 strapi.config.policies 访问。

# 如何制定策略 policy?

有几种方法可以创建一个策略 policy.

  • 使用 CLI strapi generate:policy isAuthenticated.
    阅读 CLI 文章 了解详情.
  • 手动创建一个 JavaScript 文件,名字 isAuthenticated.js 放在 ./config/policies/ 目录下.

Path — ./config/policies/isAuthenticated.js.

module.exports = async (ctx, next) => {
  if (ctx.state.user) {
    // Go to next policy or will reach the controller's action.
    return await next();
  }

  ctx.unauthorized(`You're not logged in!`);
};

在这个示例中,我们正在验证一个会话是否打开。如果是这种情况,我们调用 next() 方法来执行下一个策略或控制器的操作。否则,返回一个 401 错误。

401 HTTP 状态码表示没有登录认证

# 用法

要将策略应用于路由,需要将一组策略与其关联。有两种策略: global 策略和 scoped 策略。

WARNING

要使用 GraphQL 应用策略,请参阅 以下指南

# Global 策略

全局策略可以关联到项目中的任何路由。

Path — ./api/restaurant/routes.json.

{
  "routes": [
    {
      "method": "GET",
      "path": "/restaurants",
      "handler": "Restaurant.find",
      "config": {
        "policies": ["global::isAuthenticated"]
      }
    }
  ]
}

Restaurant.js 控制器中执行 find 操作之前,将调用位于 ./config/policies/isAuthenticated.js 中的全局策略 isAuthenticated

TIP

您可以在这个数组中放置任意多的策略,但是要注意对性能的影响。

# 插件策略

插件可以在你的应用程序中添加和公开策略。例如,插件 Users & Permissions 附带了有用的策略,以确保用户得到良好的身份验证或拥有执行操作的权限。

Path — ./api/restaurant/config/routes.json.

{
  "routes": [
    {
      "method": "GET",
      "path": "/restaurants",
      "handler": "Restaurant.find",
      "config": {
        "policies": ["plugins::users-permissions.isAuthenticated"]
      }
    }
  ]
}

位于 users-permissions 插件中的策略 isAuthenticated 将在 Restaurant.js 控制器中的 find 操作之前执行。

# 插件策略

API 策略可以与在 API 中定义的路由相关联,这些路由是在 API 中声明的。

Path — ./api/restaurant/config/policies/isAdmin.js.

module.exports = async (ctx, next) => {
  if (ctx.state.user.role.name === 'Administrator') {
    // Go to next policy or will reach the controller's action.
    return await next();
  }

  ctx.unauthorized(`You're not allowed to perform this action!`);
};

Path — ./api/restaurant/config/routes.json.

{
  "routes": [
    {
      "method": "GET",
      "path": "/restaurants",
      "handler": "Restaurant.find",
      "config": {
        "policies": ["isAdmin"]
      }
    }
  ]
}

位于 ./api/restaurant/config/policies/isAdmin.js 中的策略 isAdmin 将在 Restaurant.js 控制器中的 find 操作之前执行。

# 使用 api 之外的策略

要在另一个 api 中使用策略,可以使用以下语法引用它: {apiName}.{policyName}

Path — ./api/category/config/routes.json.

{
  "routes": [
    {
      "method": "GET",
      "path": "/categories",
      "handler": "Category.find",
      "config": {
        "policies": ["restaurant.isAdmin"]
      }
    }
  ]
}

# 高级使用

如上所述,策略在控制器的操作之前执行。它看起来像是在控制器动作 before 可以执行的动作。您还可以在 after

Path — ./config/policies/custom404.js.

module.exports = async (ctx, next) => {
  // Indicate to the server to go to
  // the next policy or to the controller's action.
  await next();

  // The code below will be executed after the controller's action.
  if (ctx.status === 404) {
    ctx.body = 'We cannot find the resource.';
  }
};

# Controllers 控制器

控制器是 JavaScript 文件,其中包含一组方法,称为客户机根据请求的路由到达的动作。这意味着每次客户机请求路由时,操作都执行业务逻辑编码并发送回响应。它们在 MVC 模式中表示 c。在大多数情况下,控制器将包含项目的大部分业务逻辑。

module.exports = {
  // GET /hello
  async index(ctx) {
    return 'Hello World!';
  },
};

在这个例子中,任何时候浏览器指向应用程序上的 /hello URL,页面都会显示文本: Hello World!.

控制器定义在每个 ./api/**/controllers/ .放在这些文件夹中的每个 JavaScript 文件都将作为控制器加载。它们也可以通过 strapi.controllersstrapi.api.**.controllers 全局变量。

# 核心控制器

当您创建一个新的 Content Type 时,您将看到一个新的空控制器已被创建。这是因为 Strapi 默认为您的模型构建了一个通用控制器,并允许您在生成的文件中覆盖和扩展它。

# 扩展模型控制器

以下是核心方法(及其当前实现)。您只需将此代码复制并粘贴到自己的控制器文件中,即可自定义方法。

WARNING

在下面的示例中,我们将假设您的控制器、服务和模型命名为 restaurant

# Utils 工具类

首先引入 'strapi-utils' 包

const { parseMultipartData, sanitizeEntity } = require('strapi-utils');
  • parseMultipartData: 此函数解析 Strapi 的 formData 格式.
  • sanitizeEntity: 此函数从模型及其关系中删除所有私有字段.
# Collection Type 集合类型
# Single Type 单一类型

# 自定义控制器

您还可以创建自定义控制器来构建自己的业务逻辑和 API Endpoint 端点。

创建控制器有两种方法:

  • 使用 CLI strapi generate:controller restaurant.
    阅读 CLI 文章 了解详情.
  • 手动创建 JavaScript 文件 ./api/**/controllers.

# 添加 Endpoints

每个控制器的操作必须是一个 async 异步函数。

每个操作都接收一个 context (ctx) 对象作为第一个参数,其中包含 request contextresponse context

# 例子

在这个例子中, 我们定义了一个路由 route 文件 ./api/hello/config/routes.json, 处理程序 Hello.index. 有关路由的详细信息,请参阅 Routing 文章

这意味着每次向服务器发送请求 GET /hello 时,Strapi 将调用 Hello.js 控制器中的 index 操作。我们的索引动作将返回 Hello World!. 您还可以返回一个 JSON 对象。

Path — ./api/hello/config/routes.json.

{
  "routes": [
    {
      "method": "GET",
      "path": "/hello",
      "handler": "Hello.index",
      "config": {
        "policies": []
      }
    }
  ]
}

Path — ./api/hello/controllers/Hello.js.

module.exports = {
  // GET /hello
  async index(ctx) {
    ctx.send('Hello World!');
  },
};

TIP

路由处理程序只能访问 ./api/**/controllers 文件夹中定义的控制器。

# Requests & Responses 请求 & 回应

# Requests 请求

上下文对象(ctx)包含所有与请求相关的信息,可以通过 ctx.request控制器策略 访问它们。

Strapi 通过 ctx.request.bodyctx.request.files 传递 body

有关更多信息,请参考 Koa 请求文档 (opens new window)

# Responses 回应

上下文对象(ctx)包含一系列用于管理服务器响应的值和函数。可以通过 ctx.responsecontrollerspolicies 访问它们。

详细信息, 访问 Koa response 文章 (opens new window).

# Services 服务

服务是一组可重用的函数。它们在尊重 DRY (不要重复自己) 编程概念和简化 controllers 逻辑方面特别有用。

# 核心服务

当您创建一个新的 Content Type 或者一个新的模型时,您将看到一个新的空服务已经被创建。这是因为 Strapi 默认为您的模型构建了一个通用服务,并允许您在生成的文件中覆盖和扩展它。

# 扩展模型服务

以下是核心方法(及其当前实现)。您只需将此代码复制并粘贴到您自己的服务文件中,即可自定义方法。

你可以在 这里 阅读 strapi.query 调用。

TIP

在下面的示例中,您的控制器、服务和模型被命名为 restaurant

# Utils 工具函数

如果你正在扩展 create 或者 update 服务,首先需要下面的工具函数:

const { isDraft } = require('strapi-utils').contentTypes;
  • isDraft: 此函数检查条目是否为草稿.
# Collection Type 集合类型
# Single Type 单一类型

# 自定义服务

您还可以创建自定义服务来构建自己的业务逻辑。

创建服务有两种方法。

  • 使用 CLI strapi generate:service restaurant.
    阅读 CLI 文章 了解详情.
  • 手动创建 JavaScript 文件 ./api/**/services/.

# 例子

服务的目标是存储可重用的函数。email 服务可以从我们的代码库中的不同功能发送电子邮件:

Path — ./api/email/services/Email.js.

const nodemailer = require('nodemailer');

// Create reusable transporter object using SMTP transport.
const transporter = nodemailer.createTransport({
  service: 'Gmail',
  auth: {
    user: 'user@gmail.com',
    pass: 'password',
  },
});

module.exports = {
  send: (from, to, subject, text) => {
    // Setup e-mail data.
    const options = {
      from,
      to,
      subject,
      text,
    };

    // Return a promise of the function that sends the email.
    return transporter.sendMail(options);
  },
};

TIP

请确保为此示例安装了 nodemailer (npm install nodemailer)。

现在可以通过 strapi.services 全局变量使用该服务。我们可以在代码库的另一部分使用它。例如下面这个控制器:

Path — ./api/user/controllers/User.js.

module.exports = {
  // GET /hello
  signup: async ctx => {
    // Store the new user in database.
    const user = await User.create(ctx.query);

    // Send an email to validate his subscriptions.
    strapi.services.email.send('welcome@mysite.com', user.email, 'Welcome', '...');

    // Send response to the server.
    ctx.send({
      ok: true,
    });
  },
};

# Queries 查询

Strapi 提供了一个实用函数 strapi.query 来进行数据库查询。

您只需调用 strapi.query('modelName', 'pluginName') 就可以访问任何模型的查询 API。

这些查询为您处理特定的 Strapi 功能,如 components, dynamic zones, filterssearch

# API 参考

# 自定义查询

当您希望自定义服务或创建新服务时,必须使用底层 ORM 模型构建查询。

要访问底层模型:

strapi.query(modelName, plugin).model;

然后您可以在模型上运行任何可用的查询。有关更多细节,您应该参考特定 ORM 文档:

# Models 模型

# 概念

# 内容类型的模型

模型是数据库结构的表示。它们被分成两个独立的文件。一个包含模型选项(例如: lifecycle hooks)的 JavaScript 文件,以及一个表示存储在数据库中的数据结构的 JSON 文件。

Path — ./api/restaurant/models/Restaurant.js.

module.exports = {
  lifecycles: {
    // Called before an entry is created
    beforeCreate(data) {},
    // Called after an entry is created
    afterCreate(result) {},
  },
};

Path — ./api/restaurant/models/Restaurant.settings.json.

{
  "kind": "collectionType",
  "connection": "default",
  "info": {
    "name": "restaurant",
    "description": "This represents the Restaurant Model"
  },
  "attributes": {
    "cover": {
      "collection": "file",
      "via": "related",
      "plugin": "upload"
    },
    "name": {
      "default": "",
      "type": "string"
    },
    "description": {
      "default": "",
      "type": "text"
    }
  }
}

在这个示例中,有一个 Restaurant 模型,其中包含了 covernamedescription 属性。

# 组件的模型

另一种类型的模型是命名 components 。组件是一种数据结构,可以在一个或多个其他 API 模型中使用。没有与生命周期相关的,只有一个 JSON 文件定义。

Path — ./components/default/simple.json

{
  "connection": "default",
  "collectionName": "components_default_simples",
  "info": {
    "name": "simple",
    "icon": "arrow-circle-right"
  },
  "options": {},
  "attributes": {
    "name": {
      "type": "string"
    }
  }
}

在这个示例中,有一个包含属性 nameSimple 组件。并且组件是 default 类别。

# 模型在哪里定义?

Content Types 内容类型模型在每个中定义 ./api/**/models/ 这些文件夹中的每个 JavaScript 或 JSON 文件都将作为模型加载。它们也可以通过 strapi.modelsstrapi.api.**.models 建立全局变量模型。在项目的任何地方都可以使用,它们包含它们所引用的 ORM 模型对象。按照惯例,模型的名称应该用小写字母书写。

Components 模型定义在 ./components 文件夹。每个组件都必须位于子文件夹(组件的类别名称)中。

# 如何创建一个模型?

TIP

如果您刚刚开始,在管理界面中直接使用 Content Type Builder 生成一些模型是非常方便的。然后,您可以在代码级别查看生成的模型映射。用户界面接管了许多验证任务,让您对可用的特性有一种感觉。

# 对于内容类型模型

使用 CLI 并运行以下命令 strapi generate:model restaurant name:string description:text

阅读 CLI 文章 了解详情。

这将创建两个位于 ./api/restaurant/models 的文件:

  • Restaurant.settings.json: 包含属性和设置列表。 JSON 格式使文件易于编辑.
  • Restaurant.js: 导入 Restaurant.settings.json 并通过附加设置和生命周期回调对其进行扩展.

TIP

当您使用 CLI (strapi generate:api <name>)创建一个新的 API 时,会自动创建一个模型。

# 对于组件模型

要创建组件,必须使用管理面板中的 Content Type Builder,因为没有组件的 cli 生成器。

或者,您可以通过遵循前面描述的文件路径并遵循下面描述的文件结构来手动创建组件。

# 模型设置

可以在模型上设置其他设置:

  • kind (string) - 定义模型是否为集合类型 (collectionType) 的单一类别 (singleType) - 只适用于内容类型
  • connection (string) - 必须使用的连接名称. 默认值: default.
  • collectionName (string) - 存储数据的集合名称(或表名称).
  • globalId (string) - 此模型的全局变量名 (case-sensitive) - 只适用于内容类型
  • attributes (object) - 定义模型的数据结构. 查找可用的选项 below.

Path — Restaurant.settings.json.

{
  "kind": "collectionType",
  "connection": "mongo",
  "collectionName": "Restaurants_v1",
  "globalId": "Restaurants",
  "attributes": {}
}

在本例中,可以通过 Restaurants 全局变量访问模型 Restaurant。数据将存储在 Restaurants_v1 mongo 集合或表中,模型将使用 ./config/database.js

WARNING

如果没有在 JSON 文件中手动设置,Strapi 将采用文件名 globalIdglobalId 作为关系和 Strapi api 中模型的参考。如果您选择重命名它(通过重命名文件或更改 globalId 的值) ,则必须手动迁移表并更新引用。请注意,您不应该更改 Strapi 的模型 globalId (插件和核心模型) ,因为它们直接用于 Strapi api 和其他模型的关系中。

TIP

可以随时更改 connection 值,但是应该注意,没有自动数据迁移过程。另外,如果新连接不使用相同的 ORM,那么您将不得不重写查询。

# 模型资料

model-json 表示关于模型的信息。当显示模型时,此信息用于管理界面。

  • name: 模型的名称,如管理界面中所示.
  • description: 模型的描述.
  • icon: fontawesome V5 的名字 - 只适用于组件

Path — Restaurant.settings.json.

{
  "info": {
    "name": "restaurant",
    "description": ""
  }
}

# 模型选项

模型上的选项键 model-json 状态。

  • timestamps: 时间戳, 这告诉模型使用哪些属性作为时间戳。接受 boolean 或字符串 Array ,其中第一个元素为 create date,第二个元素为 update date。对于 Bookshelf 设置为 true 时的默认值是 ["created_at", "updated_at"] ,对于 MongoDB 设置为 ["createdAt", "updatedAt"]
  • privateAttributes: 这种配置允许将一组属性视为私有属性,即使它们实际上没有在模型中定义为属性。接受字符串 Array 。当使用 MongoDB 时,它可以用来从 API 响应中删除时间戳或 _v 。模型中定义的 privateAttributes 集与全局 Strapi 配置中定义的 privateAttributes 合并。
  • populateCreatorFields: 配置 API 响应是否应该包含 created_byupdated_by 字段。接受一个布尔值。默认值为 false
  • draftAndPublish: 启用草稿和发布特性。接受一个 boolean。默认值为 false

Path — Restaurant.settings.json.

{
  "options": {
    "timestamps": true,
    "privateAttributes": ["id", "created_at"],
    "populateCreatorFields": true,
    "draftAndPublish": false
  }
}

# 定义属性

目前有以下几种类型:

  • string
  • text
  • richtext
  • email
  • password
  • integer
  • biginteger
  • float
  • decimal
  • date
  • time
  • datetime
  • boolean
  • enumeration
  • json
  • uid

# Validations 验证

您可以对属性应用基本的验证。以下支持的验证只有 MongoDB 数据库连接支持。如果使用 SQL 数据库,应该使用本地 SQL 约束来应用它们。

  • required (boolean) — 如果为真,则为此属性添加一个必需的验证器.
  • unique (boolean) — 是否在此属性上定义唯一索引.
  • index (boolean) — 在此属性上添加索引,这将创建一个 single field index (opens new window) 可以在后台运行. 只支持 MongoDB.
  • max (integer) — 检查值是否大于或等于给定的最大值.
  • min (integer) — 检查值是否小于或等于给定的最小值.

安全验证

为了改善开发人员在开发或使用管理面板时的体验,该框架通过以下这些“安全验证”来增强属性:

  • private (boolean) — 如果为真,属性将从服务器响应中删除. (这对隐藏敏感数据很有用).
  • configurable (boolean) - 如果属性为 false,则无法从 Content Type Builder 插件配置该属性.
  • autoPopulate (boolean) - 如果为 false,相关数据将不会在 REST 响应中填充。(这不会停止查询 GraphQL 上的关系数据)

# 例外

uid

  • targetField(string) — The value is the name of an attribute that has string of the text type.
  • options (string) — 该值是传递给 the underlying uid generator (opens new window). 一个警告是 uid 必须遵守以下正则表达式 /^[A-Za-z0-9-_.~]*$.

# 例子

Path — Restaurant.settings.json.

{
  ...
  "attributes": {
    "title": {
      "type": "string",
      "min": 3,
      "max": 99,
      "unique": true
    },
    "description": {
      "default": "My description",
      "type": "text",
      "required": true
    },
    "slug": {
      "type": "uid",
      "targetField": "title"
    }
    ...
  }
}

# Relations 关系

关系允许您在内容类型之间创建链接(关系)。

# Components 组件

组件字段允许您在内容类型和组件结构之间创建关系。

# 例子

让我们说,我们创建了一个 restaurant 类的 openinghours 组件。

Path — ./api/restaurant/models/Restaurant.settings.json.

{
  "attributes": {
    "openinghours": {
      "type": "component",
      "repeatable": true,
      "component": "restaurant.openinghours"
    }
  }
}
  • repeatable (boolean): Could be true or false that let you create a list of data.
  • component (string): It follows this format <category>.<componentName>.

# Dynamic Zone

Dynamic Zone 字段允许您创建一个灵活的空间,以便根据组件的混合列表编写内容。

# 例子

假设我们在 article 类别中创建了一个 slidercontent 组件。

Path — ./api/article/models/Article.settings.json.

{
  "attributes": {
    "body": {
      "type": "dynamiczone",
      "components": ["article.slider", "article.content"]
    }
  }
}
  • components (array): 遵循此格式的组件数组 <category>.<componentName>.

# 生命周期挂钩

生命周期钩子是在调用 Strapi queries 时触发的函数。当您在管理面板中管理内容或使用 queries 开发自定义代码时,它们将自动触发

要配置 ContentType 生命周期钩子,可以在 {modelName}.js 中设置 lifecycles 键。文件位于 ./api/{apiName}/models 文件夹。

# 可用的生命周期挂钩

# 例子

Path — ./api/restaurant/models/Restaurant.js.

module.exports = {
  /**
   * Triggered before user creation.
   */
  lifecycles: {
    async beforeCreate(data) {
      data.isTableFull = data.numOfPeople === 4;
    },
  },
};

TIP

您可以改变其中一个参数来改变它的属性。确保不要重新分配参数,因为它不会产生任何效果:

This will Work

module.exports = {
  lifecycles: {
    beforeCreate(data) {
      data.name = 'Some fixed name';
    },
  },
};

This will NOT Work

module.exports = {
  lifecycles: {
    beforeCreate(data) {
      data = {
        ...data,
        name: 'Some fixed name',
      };
    },
  },
};

# 定制使用

当您构建定制 ORM 特定查询时,生命周期将不会被触发。然而,如果您愿意,您可以直接调用生命周期函数。

Bookshelf example

Path - ./api/{apiName}/services/{serviceName}.js

module.exports = {
  async createCustomEntry() {
    const ORMModel = strapi.query(modelName).model;

    const newCustomEntry = await ORMModel.forge().save();

    // trigger manually
    ORMModel.lifecycles.afterCreate(newCustomEntry.toJSON());
  },
};

TIP

当直接调用生命周期函数时,您需要确保使用预期的参数调用它。

# Webhooks

Webhook 是应用程序用于通知其他应用程序发生事件的构造。更准确地说,webhook 是一个用户定义的 HTTP 回调。使用 webhook 是告诉第三方提供者开始某些处理(CI、构建、部署...)的好方法。

Webhook 的工作方式是通过 HTTP 请求(通常是 POST 请求)将信息传递给接收应用程序。

# 用户内容类型的网钩

为了防止无意中将任何用户信息发送到其他应用程序,Webhooks 不适用于 User 内容类型。如果需要通知其他应用程序用户集合中的更改,可以通过在文件中创建 Lifecycle hooks 来实现 ./extensions/users-permissions/models/User.js .

# 可用配置

您可以在文件 ./config/server.js 中设置 webhook 配置。

  • webhooks
    • defaultHeaders: 你可以设置默认的标题来使用你的 webhook 请求。此选项被 webhook 本身中设置的头部覆盖.

示例 configuration

module.exports = {
  webhooks: {
    defaultHeaders: {
      'Custom-Header': 'my-custom-header',
    },
  },
};

# 安全 webhooks

大多数时候,webhooks 对公共 URL 发出请求,因此有可能有人发现这个 URL 并发送错误的信息。

为了防止这种情况发生,您可以使用认证标记发送标头。使用管理面板,你必须为每一个 webhook 做到这一点。另一种方法是定义 defaultHeaders 来添加到每个 webhook 请求中。

您可以通过更新 ./config/server.js 文件来配置这些全局头文件:

如果您正在自己开发 webhook 处理程序,现在可以通过读取标头来验证标记。

# 可用事件

默认情况下,Strapi webhooks 可以由以下事件触发:

Name Description
entry.create Triggered when a Content Type entry is created.
entry.update Triggered when a Content Type entry is updated.
entry.delete Triggered when a Content Type entry is deleted.
entry.publish Triggered when a Content Type entry is published.*
entry.unpublish Triggered when a Content Type entry is unpublished.*
media.create Triggered when a media is created.
media.update Triggered when a media is updated.
media.delete Triggered when a media is deleted.

* 仅当在此内容类型上启用 draftAndPublish 时。

# Payloads

NOTE

私有字段和密码不会在有效负载中发送。

# Headers

当有效负载传递到你的 webhook 的 URL 时,它将包含特定的头部:

Header Description
X-Strapi-Event Name of the event type that was triggered.

# entry.create

创建新条目时触发此事件。

示例 payload

{
  "event": "entry.create",
  "created_at": "2020-01-10T08:47:36.649Z",
  "model": "address",
  "entry": {
    "id": 1,
    "geolocation": {},
    "city": "Paris",
    "postal_code": null,
    "category": null,
    "full_name": "Paris",
    "created_at": "2020-01-10T08:47:36.264Z",
    "updated_at": "2020-01-10T08:47:36.264Z",
    "cover": null,
    "images": []
  }
}

# entry.update

更新条目时触发此事件。

示例 payload

{
  "event": "entry.update",
  "created_at": "2020-01-10T08:58:26.563Z",
  "model": "address",
  "entry": {
    "id": 1,
    "geolocation": {},
    "city": "Paris",
    "postal_code": null,
    "category": null,
    "full_name": "Paris",
    "created_at": "2020-01-10T08:47:36.264Z",
    "updated_at": "2020-01-10T08:58:26.210Z",
    "cover": null,
    "images": []
  }
}

# entry.delete

删除条目时触发此事件。

示例 payload

{
  "event": "entry.delete",
  "created_at": "2020-01-10T08:59:35.796Z",
  "model": "address",
  "entry": {
    "id": 1,
    "geolocation": {},
    "city": "Paris",
    "postal_code": null,
    "category": null,
    "full_name": "Paris",
    "created_at": "2020-01-10T08:47:36.264Z",
    "updated_at": "2020-01-10T08:58:26.210Z",
    "cover": null,
    "images": []
  }
}

# entry.publish

发布条目时触发此事件。

示例 payload

{
  "event": "entry.publish",
  "created_at": "2020-01-10T08:59:35.796Z",
  "model": "address",
  "entry": {
    "id": 1,
    "geolocation": {},
    "city": "Paris",
    "postal_code": null,
    "category": null,
    "full_name": "Paris",
    "created_at": "2020-01-10T08:47:36.264Z",
    "updated_at": "2020-01-10T08:58:26.210Z",
    "published_at": "2020-08-29T14:20:12.134Z",
    "cover": null,
    "images": []
  }
}

# entry.unpublish

未发布条目时触发此事件。

示例 payload

{
  "event": "entry.unpublish",
  "created_at": "2020-01-10T08:59:35.796Z",
  "model": "address",
  "entry": {
    "id": 1,
    "geolocation": {},
    "city": "Paris",
    "postal_code": null,
    "category": null,
    "full_name": "Paris",
    "created_at": "2020-01-10T08:47:36.264Z",
    "updated_at": "2020-01-10T08:58:26.210Z",
    "published_at": null,
    "cover": null,
    "images": []
  }
}

# media.create

当您在创建条目时或通过媒体界面上传文件时,将触发此事件。

示例 payload

{
  "event": "media.create",
  "created_at": "2020-01-10T10:58:41.115Z",
  "media": {
    "id": 1,
    "name": "image.png",
    "hash": "353fc98a19e44da9acf61d71b11895f9",
    "sha256": "huGUaFJhmcZRHLcxeQNKblh53vtSUXYaB16WSOe0Bdc",
    "ext": ".png",
    "mime": "image/png",
    "size": 228.19,
    "url": "/uploads/353fc98a19e44da9acf61d71b11895f9.png",
    "provider": "local",
    "provider_metadata": null,
    "created_at": "2020-01-10T10:58:41.095Z",
    "updated_at": "2020-01-10T10:58:41.095Z",
    "related": []
  }
}

# media.update

当您替换媒体或通过媒体界面更新媒体的元数据时,将触发此事件。

示例 payload

{
  "event": "media.update",
  "created_at": "2020-01-10T10:58:41.115Z",
  "media": {
    "id": 1,
    "name": "image.png",
    "hash": "353fc98a19e44da9acf61d71b11895f9",
    "sha256": "huGUaFJhmcZRHLcxeQNKblh53vtSUXYaB16WSOe0Bdc",
    "ext": ".png",
    "mime": "image/png",
    "size": 228.19,
    "url": "/uploads/353fc98a19e44da9acf61d71b11895f9.png",
    "provider": "local",
    "provider_metadata": null,
    "created_at": "2020-01-10T10:58:41.095Z",
    "updated_at": "2020-01-10T10:58:41.095Z",
    "related": []
  }
}

# media.delete

只有在通过媒体界面删除媒体时才会触发此事件。

示例 payload

{
  "event": "media.delete",
  "created_at": "2020-01-10T11:02:46.232Z",
  "media": {
    "id": 11,
    "name": "photo.png",
    "hash": "43761478513a4c47a5fd4a03178cfccb",
    "sha256": "HrpDOKLFoSocilA6B0_icA9XXTSPR9heekt2SsHTZZE",
    "ext": ".png",
    "mime": "image/png",
    "size": 4947.76,
    "url": "/uploads/43761478513a4c47a5fd4a03178cfccb.png",
    "provider": "local",
    "provider_metadata": null,
    "created_at": "2020-01-07T19:34:32.168Z",
    "updated_at": "2020-01-07T19:34:32.168Z",
    "related": []
  }
}