自定义策略

了解如何使用我们的 FoodAdvisor 示例创建自定义政策

示例手册:自定义策略

此页面是后端自定义示例手册的一部分。请确保您已阅读其简介

开箱即用,FoodAdvisor 不使用任何自定义策略或路由中间件来控制对内容类型端点的访问。

在 Strapi 中,可以使用策略或路由中间件来控制对内容类型端点的访问:

  • 策略是只读的,允许请求传递或返回错误,
  • 而路由中间件可以执行其他逻辑。

在我们的示例中,让我们使用策略。

创建自定义策略

💭 上下文:

假设我们想自定义 FoodAdvisor 的后端,以防止餐厅老板使用前端网站上的 之前创建的表单 为其业务创建虚假评论。

🎯 目标

  1. 为仅应用于“评论”集合类型的策略创建一个新文件夹。
  2. 创建新的策略文件。
  3. 当到达 /reviews 端点时,使用实体服务 API 中的 findMany() 方法获取有关餐厅所有者的信息。
  4. 如果经过身份验证的用户是餐厅所有者,则返回错误,或者在其他情况下让请求通过。

更多信息请参阅 策略路由实体服务 API 文档。

🧑‍💻 代码示例:

FoodAdvisor 项目的 /api 文件夹中,创建一个新的 src/api/review/policies/is-owner-review.js 文件,其中包含以下代码:

src/api/review/policies/is-owner-review.js
jsx
module.exports = async (policyContext, config, { strapi }) => {
  const { body } = policyContext.request
  const { user } = policyContext.state

  // 如果请求中没有经过身份验证的用户,则返回错误
  if (!user) {
    return false
  }
  /**
   * 查询 Restaurants 集合类型
   * 使用 Entity Service API
   * 检索有关餐厅所有者的信息。
   */
  const [restaurant] = await strapi.entityService.findMany(
    'api::restaurant.restaurant',
    {
      filters: {
        slug: body.restaurant,
      },
      populate: ['owner'],
    }
  )
  if (!restaurant) {
    return false
  }

  /**
   * 如果提交请求的用户是餐厅老板,
   * 我们不允许创建评论。
   */
  if (user.id === restaurant.owner.id) {
    return false
  }

  return true
}

应在路由配置中声明策略或路由中间件以实际控制访问。有关路由的更多信息,请参阅参考文档,或查看路由手册中的示例。

通过策略发送自定义错误

💭 上下文:

开箱即用,FoodAdvisor 在策略拒绝访问路由时发送默认错误。假设我们想要自定义在先前创建的自定义策略不允许创建评论时发送的错误。

🎯 目标:

配置自定义策略以抛出自定义错误而不是默认错误。

更多信息可以在 错误处理 文档中找到。

🧑‍💻 代码示例:

FoodAdvisor 项目的 /api 文件夹中,更新 之前创建的 is-owner-review 自定义策略,如下所示(突出显示的行是唯一修改的行):

src/api/review/policies/is-owner-review.js
jsx
const { errors } = require('@strapi/utils')
const { PolicyError } = errors

module.exports = async (policyContext, config, { strapi }) => {
  const { body } = policyContext.request
  const { user } = policyContext.state

  // 如果请求中没有经过身份验证的用户,则返回错误
  if (!user) {
    return false
  }
  /**
   * 查询 Restaurants 集合类型
   * 使用 Entity Service API
   * 检索有关餐厅所有者的信息。
   */
  const filteredRestaurants = await strapi.entityService.findMany(
    'api::restaurant.restaurant',
    {
      filters: {
        slug: body.restaurant,
      },
      populate: ['owner'],
    }
  )

  const restaurant = filteredRestaurants[0]

  if (!restaurant) {
    return false
  }

  /**
   * 如果提交请求的用户是餐厅老板,
   * 我们不允许创建评论。
   */
  if (user.id === restaurant.owner.id) {
    // highlight-start
    /**
     * 抛出自定义策略错误
     * 而不是仅仅返回 false
     * (这会导致一般策略错误)。
     */
    const error = new ApplicationError(
      'The owner of the restaurant cannot submit reviews',
      {
        policy: 'is-owner-review',
        errCode: 'RESTAURANT_OWNER_REVIEW', // 有助于识别前端的不同错误
      }
    )
    error.name = 'OwnerReviewError'
    throw error
    // highlight-end
  }

  return true
}
发送的默认策略错误与自定义策略错误的响应:
详情

在前端使用自定义错误

💭 上下文:

开箱即用,FoodAdvisor 提供的 Next.js 支持的前端网站在访问内容时不会在前端网站上显示错误或成功消息。例如,当无法使用 之前创建的表单 添加新评论时,网站不会通知用户。

假设我们想要自定义 FoodAdvisor 的前端以捕获 之前创建的自定义策略 抛出的自定义错误,并使用 React Hot Toast 通知 将其显示给用户。作为奖励,成功创建评论后将显示另一个 toast 通知。

餐厅老板无法提交评论

当餐厅老板尝试提交新的评论时,REST API 响应会返回自定义错误,今后前端网站上会显示 toast 通知。

🎯 目标:

  • 捕获前端网站上的错误并在通知中显示。
  • 如果政策允许创建新评论,则发送另一条通知。

🧑‍💻 代码示例:

FoodAdvisor 项目的 /client 文件夹中,您可以更新 之前创建的 new-review 组件,如下所示(已修改的行已突出显示):

显示自定义错误或成功创建评论的 toast 通知的示例前端代码:
详情
下一步是什么?

详细了解如何配置 自定义路由 以使用自定义策略,以及如何使用这些自定义路由来调整基于 Strapi 的应用程序。