Rails JS链接在控制器处理两次

时间:2014-07-02 20:55:50

标签: jquery ruby-on-rails ajax ruby-on-rails-4

在我正在编码的应用中看到一些奇怪的行为。我有一个索引页面,显示记录列表。如果我创建一个新记录,它将被插入到数据库中,然后用户被重定向到索引页面,该页面现在显示新记录。每条记录都有一些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的事实似乎很重要,但我无法想象它为什么只会发生新的条目。

谢谢!

1 个答案:

答案 0 :(得分:1)

如果它来自一个单独的位置,那么它不会来自您的代码。它可能是病毒,设置严重的缓存代理,Web扫描程序机器人或其他任何东西。

保护你的代码免受双重输入是一件好事,但看起来相当复杂。示例:由于负载平衡代理,您不能依赖源IP。

无论如何你可以尝试(我认为你必须):

1-您正在使用GET请求,我从您的日志中看到您的数据库中已完成INSERT。这违反了RESTfull解决方案。只有POST请求才能触发INSERT。您应该更改代码以反映这一点,无论这有多难

2-正确设置该操作的POST请求后,必须强制使用rails来防止跨标记请求,方法是在标题中使用csrf_meta_tags,并在控制器中使用protect_from_forgery

我希望这可以解决问题,并放弃第二个请求。在开始进行此类修改之前,请确保您的测试套件完全覆盖了您的代码,因为这种深度更改可能会导致特别是AJAX表单或多部分表单的意外结果。

这很复杂而且可能很长,但是现在我看到你的日志似乎你的服务器没有受到保护。