在我正在编码的应用中看到一些奇怪的行为。我有一个索引页面,显示记录列表。如果我创建一个新记录,它将被插入到数据库中,然后用户被重定向到索引页面,该页面现在显示新记录。每条记录都有一些AJAX投票链接。当用户点击新项目上的投票链接时,我看到控制器使用两个不同的IP地址处理了两次请求...另外,我将UUID存储在每个用户的永久cookie中,第二个请求来自第二个IP地址显示的UUID与当前用户不同。
我只是在朋友的办公室看到了这个问题,所以我不知道是不是因为他们搞乱了网络或其他东西(公司网络有代理/防火墙等)。 ..我告诉他们管理不善)。我认为奇怪的是它只发生在新记录上,而不是在预先存在的记录上点击投票链接(使用相同的确切代码)时...
此处显示控制器响应的日志 - 请注意,处理了一个响应as JS
,第二个as */*
:
Started GET "/vote/14?vote_value=-1" for <company's external IP> at 2014-07-02 16:31:41 -0400
Processing by FoosController#voting as JS
Parameters: {"vote_value"=>"-1", "foo_id"=>"14"}
Foo Load (0.7ms) SELECT "food".* FROM "food" WHERE "food"."id" = $1 ORDER BY created_at DESC LIMIT 1 [["id", "14"]]
(0.3ms) BEGIN
SQL (0.8ms) INSERT INTO "votes" ("created_at", "updated_at", "uuid", "value", "foo_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Wed, 02 Jul 2014 20:31:41 UTC +00:00], ["updated_at", Wed, 02 Jul 2014 20:31:41 UTC +00:00], ["uuid", "61230391-311a-4358-9c4b-665d2a8bc8e9"], ["value", -1], ["foo_id", 14]]
(2.0ms) COMMIT
(0.2ms) BEGIN
(0.3ms) COMMIT
Rendered foo/voting.js.erb (0.1ms)
Completed 200 OK in 14ms (Views: 2.2ms | ActiveRecord: 4.3ms)
Started GET "/vote/14?vote_value=-1" for <different unrecognized IP> at 2014-07-02 16:31:41 -0400
Processing by FoosController#voting as */*
Parameters: {"vote_value"=>"-1", "foo_id"=>"14"}
Foo Load (0.6ms) SELECT "food".* FROM "food" WHERE "food"."id" = $1 ORDER BY created_at DESC LIMIT 1 [["id", "14"]]
(0.3ms) BEGIN
SQL (0.7ms) INSERT INTO "votes" ("created_at", "updated_at", "uuid", "value", "foo_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Wed, 02 Jul 2014 20:31:41 UTC +00:00], ["updated_at", Wed, 02 Jul 2014 20:31:41 UTC +00:00], ["uuid", "b8a23470-9f9f-4a65-8577-7da7e31a6995"], ["value", -1], ["foo_id", 14]]
(1.7ms) COMMIT
(0.1ms) BEGIN
(0.1ms) COMMIT
Rendered foo/voting.js.erb (0.2ms)
Completed 200 OK in 13ms (Views: 2.4ms | ActiveRecord: 3.5ms)
这里是包含AJAX链接的记录的部分内容:
<div class="foo" id="foo-<%= foo.id %>">
<div class="voting">
<span id="foo-<%= foo.id %>-score"><%= foo.score %></span>
<div class="voting-links" id="foo-<%= foo.id %>-voting-links">
<%= link_to 'UP', voting_path(foo.id, vote_value: 1), remote: true %> /
<%= link_to 'DOWN', voting_path(foo.id, vote_value: -1), remote: true %>
</div>
</div>
<div class="foo-content" id="foo-<%= foo.id %>-content">
<%= foo.content %>
</div>
</div>
这是我的控制器动作:
def voting
@foo = Foo.find(params[:foo_id])
vote = @foo.votes.create(uuid: cookies[:user_uuid], value: params[:vote_value])
@foo.save
respond_to do |format|
format.html { redirect_to root_path }
format.js { render 'voting' }
end
end
响应js.erb文件:
score = $('#foo-' + <%= @foo.id %> + '-score');
score.html(<%= @foo.score %>);
有关如何解决此问题或确保它不是应用程序问题的任何想法,并且是此特定站点的网络问题?我还没有能够在我的办公室里复制。我使用Firebug来检查从浏览器发出的请求,看起来只有一个请求被发送出去。这只是一个错误配置的网关/路由器/防火墙等问题......以某种方式再现请求?它发送超过两个不同的UUID的事实似乎很重要,但我无法想象它为什么只会发生新的条目。
谢谢!
答案 0 :(得分:1)
如果它来自一个单独的位置,那么它不会来自您的代码。它可能是病毒,设置严重的缓存代理,Web扫描程序机器人或其他任何东西。
保护你的代码免受双重输入是一件好事,但看起来相当复杂。示例:由于负载平衡代理,您不能依赖源IP。
无论如何你可以尝试(我认为你必须):
1-您正在使用GET请求,我从您的日志中看到您的数据库中已完成INSERT。这违反了RESTfull解决方案。只有POST请求才能触发INSERT。您应该更改代码以反映这一点,无论这有多难
2-正确设置该操作的POST请求后,必须强制使用rails来防止跨标记请求,方法是在标题中使用csrf_meta_tags
,并在控制器中使用protect_from_forgery
。
我希望这可以解决问题,并放弃第二个请求。在开始进行此类修改之前,请确保您的测试套件完全覆盖了您的代码,因为这种深度更改可能会导致特别是AJAX表单或多部分表单的意外结果。
这很复杂而且可能很长,但是现在我看到你的日志似乎你的服务器没有受到保护。