模型和服务垃圾邮件防护及 CAPTCHA 支持
在向 REST API、GraphQL API 或 Web UI 添加任何垃圾邮件或 CAPTCHA 支持之前,你必须 首先向以下部分添加必要的支持:
- 后端 ActiveRecord 模型。
- 服务层。
无论你支持的是哪种类型的垃圾邮件或 CAPTCHA 请求实现,以下大部分或全部更改都是必需的。一些完全基于 GraphQL API 的新功能可能没有控制器,也不需要你在控制器中添加 mark_as_spam 操作。
为此:
- 向 ActiveRecord 模型添加
Spammable支持。 - 向控制器添加对
mark_as_spam操作的支持。 - 向服务的 execute 方法添加对
check_for_spam的调用。
向 ActiveRecord 模型添加 Spammable 支持
-
在模型类中包含
Spammable模块:include Spammable -
添加:
attr_spammable来指示哪些字段可以检查垃圾邮件。每个模型最多支持两个字段:一个 “title” 和一个 “description"。你可以指定哪些字段被视为 “title” 或 “description"。例如,这行代码将content字段指定为description:attr_spammable :content, spam_description: true -
添加
#check_for_spam?方法的实现:def check_for_spam?(user:) # 根据各种适用的检查返回布尔结果,这些检查可能包括 # 哪些属性已更改、用户类型、数据是否公开可见 # 以及其他标准。这可能因模型类型而异, # 并可能随着垃圾邮件检查要求的发展而变化。 end参考其他现有
Spammable模型中此方法的实现, 以了解所需逻辑检查的示例。
向控制器添加对 mark_as_spam 操作的支持
SpammableActions::AkismetMarkAsSpamAction 模块为控制器添加了对 #mark_as_spam 操作的支持。
该控制器允许管理员在 Admin 区域的 Spam log 部分 中管理相关 Spammable 模型的垃圾邮件。
-
在控制器中包含
SpammableActions::AkismetMarkAsSpamAction模块。include SpammableActions::AkismetMarkAsSpamAction -
添加
#spammable_path方法的实现。垃圾邮件管理页面在编辑后会重定向到此页面。 参考其他现有控制器中此方法的实现,以了解所需的路径逻辑类型。一般来说, 它应该是Spammable模型控制器的#show操作。def spammable_path widget_path(widget) end
根据功能的实现方式,控制器可能还需要其他更改。有关更多详细信息,请参阅 Web UI。
向服务的 execute 方法添加对 check_for_spam 的调用
此方法适用于任何可以持久化可垃圾邮件检查属性的服务:
-
在
app/services下的相关 Create 或 Update 服务中,在模型上调用check_for_spam方法。 -
如果垃圾邮件检查失败:
- 会在模型中添加错误,导致模型无效并阻止其保存。
needs_recaptcha属性被设置为true。
对模型的这些更改使其能够被后续的后端和前端 CAPTCHA 逻辑处理。
对每个相关服务进行这些更改:
- 在
execute方法中,在模型上调用check_for_spam方法。 (如果服务使用该模式,你也可以使用before_create或before_update。)此方法使用命名参数, 因此如果你参考现有示例,其用法会很清楚。但是,有两个重要注意事项:check_for_spam必须在所有必要的更改都应用到未保存的(脏的)Spammable模型实例之后执行。 此顺序确保存在可垃圾邮件检查的属性。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