I have a rails model called ant.keyalias
for which I have a ProficiencyTest
validation for the uniqueness
field of the person taking the test.
There is a page where the user fills out there information,
then once that information is entered, and the email is validated as unique, it , calls email
and renders the test path.
The test path shows a simple form, and upon completion of the form the ProficiencyTest.create
is called on the update_attributes
at the id created by @proficiency_test
, the changes are stored by create
, the ActionMailer sends an email to the apposite recipients of the test result, and finally a third view is rendered which basically displays test results.
This works great, under normal circumstances, if someone logs in to take the tests on Page 1, and they have already logged in, Page 1 is re-rendered with the @proficiency_test.save
set to display validation errors.
The problem is, it is very easy to simulate someone (or a web bot) spamming the form by simply navigating Back on the browser to Page 2 where the form is presented and hit submit again which triggers another submit and fires the ActionMailer successfully.
Here is a version of my flash.now
controller:
https://gist.github.com/anonymous/ce169784c7889d6543c6
And here is my ProficiencyTest model:
https://gist.github.com/anonymous/a971ce8a2d75190f9486
How can I ensure that once a proficiency_test of a single id has been saved from the first ProficiencyTest
that the submit button on Page 2 doesn't trigger another save and another call to update_attributes
?
答案 0 :(得分:2)
好的最简单的方法是,您应该发送邮件以响应模型状态更改,而不是HTTP请求。所以当有人点击"提交"或点击后退按钮并再次提交,它们会触发新的HTTP请求,但不一定是模型状态更改。
我从上面显示的代码中看到您实际在ProficiencyTest#grade
方法中触发了邮件程序。一般来说,在模型方法中触发外部动作并不是一种好习惯,尤其不是你想要的同义词。
我认为对你来说更好的做法是将邮件程序回调等模型放入控制器中,然后编写控制器方法,使测试只能提交一次。所以你的方法看起来应该更像这样:
def grade
@proficiency_test = ProficiencyTest.find(params[:id])
if @proficiency_test.level # the test was already taken
# redirect back or show error message
else
@proficiency_test.update_attributes(proficiency_test_params)
@proficiency_test.grade! # this should grade and save the test, but not do mailers
# send mailers here
# redirect or render success view here
end
end
至于发送邮件,如果从模型中提取出来,可以使其更加清晰。例如,您可以简单地将熟练度测试对象传递给那些邮件程序,然后在邮件程序操作中阅读熟练度测试中的属性。
希望这有帮助。