Help us learn about your current experience with the documentation. Take the survey.

模型和服务垃圾邮件防护及 CAPTCHA 支持

在向 REST API、GraphQL API 或 Web UI 添加任何垃圾邮件或 CAPTCHA 支持之前,你必须 首先向以下部分添加必要的支持:

  1. 后端 ActiveRecord 模型。
  2. 服务层。

无论你支持的是哪种类型的垃圾邮件或 CAPTCHA 请求实现,以下大部分或全部更改都是必需的。一些完全基于 GraphQL API 的新功能可能没有控制器,也不需要你在控制器中添加 mark_as_spam 操作。

为此:

  1. 向 ActiveRecord 模型添加 Spammable 支持
  2. 向控制器添加对 mark_as_spam 操作的支持
  3. 向服务的 execute 方法添加对 check_for_spam 的调用

向 ActiveRecord 模型添加 Spammable 支持

  1. 在模型类中包含 Spammable 模块:

    include Spammable
  2. 添加:attr_spammable 来指示哪些字段可以检查垃圾邮件。每个模型最多支持两个字段:一个 “title” 和一个 “description"。你可以指定哪些字段被视为 “title” 或 “description"。例如,这行代码将 content 字段指定为 description

    attr_spammable :content, spam_description: true
  3. 添加 #check_for_spam? 方法的实现:

    def check_for_spam?(user:)
      # 根据各种适用的检查返回布尔结果,这些检查可能包括
      # 哪些属性已更改、用户类型、数据是否公开可见
      # 以及其他标准。这可能因模型类型而异,
      # 并可能随着垃圾邮件检查要求的发展而变化。
    end

    参考其他现有 Spammable 模型中此方法的实现, 以了解所需逻辑检查的示例。

向控制器添加对 mark_as_spam 操作的支持

SpammableActions::AkismetMarkAsSpamAction 模块为控制器添加了对 #mark_as_spam 操作的支持。 该控制器允许管理员在 Admin 区域的 Spam log 部分 中管理相关 Spammable 模型的垃圾邮件。

  1. 在控制器中包含 SpammableActions::AkismetMarkAsSpamAction 模块。

    include SpammableActions::AkismetMarkAsSpamAction
  2. 添加 #spammable_path 方法的实现。垃圾邮件管理页面在编辑后会重定向到此页面。 参考其他现有控制器中此方法的实现,以了解所需的路径逻辑类型。一般来说, 它应该是 Spammable 模型控制器的 #show 操作。

    def spammable_path
      widget_path(widget)
    end

根据功能的实现方式,控制器可能还需要其他更改。有关更多详细信息,请参阅 Web UI

向服务的 execute 方法添加对 check_for_spam 的调用

此方法适用于任何可以持久化可垃圾邮件检查属性的服务:

  1. app/services 下的相关 Create 或 Update 服务中,在模型上调用 check_for_spam 方法。

  2. 如果垃圾邮件检查失败:

    • 会在模型中添加错误,导致模型无效并阻止其保存。
    • needs_recaptcha 属性被设置为 true

    对模型的这些更改使其能够被后续的后端和前端 CAPTCHA 逻辑处理。

对每个相关服务进行这些更改:

  1. execute 方法中,在模型上调用 check_for_spam 方法。 (如果服务使用该模式,你也可以使用 before_createbefore_update。)此方法使用命名参数, 因此如果你参考现有示例,其用法会很清楚。但是,有两个重要注意事项:
    1. check_for_spam 必须在所有必要的更改都应用到未保存的(脏的)Spammable 模型实例之后执行。 此顺序确保存在可垃圾邮件检查的属性。
    2. check_for_spam 必须在检查模型错误和尝试 save 之前执行。 如果在模型的更改属性中检测到潜在的垃圾邮件,我们必须阻止保存。
module Widget
  class CreateService < ::Widget::BaseService
    # 注意:我们为 `perform_spam_check` 添加默认值 `true`,因为垃圾邮件检查很可能是必需的。
    def initialize(project:, current_user: nil, params: {}, perform_spam_check: true)
      super(project: project, current_user: current_user, params: params)

      @perform_spam_check = perform_spam_check
    end

    def execute
      widget = Widget::BuildService.new(project, current_user, params).execute

      # 更多可能在垃圾邮件检查前操作脏模型的代码。

      # 注意:在可垃圾邮件检查模型实例化之后,但在
      # 验证或保存之前执行此操作。
      widget.check_for_spam(user: current_user, action: :create) if perform_spam_check

      # 可能更多与保存模型相关的代码,但不应该更改任何属性。

      widget.save
    end

    private

    attr_reader :perform_spam_check