【问题标题】:Rails accepting duplicate POST requestsRails 接受重复的 POST 请求
【发布时间】:2020-10-19 22:05:06
【问题描述】:

我的 rails 应用程序有一个奇怪的问题。我的应用程序在一秒钟内接受重复的 POST 请求

这个包含相同数据的重复请求奇怪地能够绕过我的模型的唯一性验证。这导致创建具有完全相同内容的两行数据。

真正让我感到困惑的是,从昨天开始,它每天只发生一次,我不确定是什么原因造成的。 (系统已经上线,我的客户正在使用,这个方法调用一天使用200-300次,我根本无法重现)

这是我的代码片段和完整代码链接的情况,按时间顺序排列

  1. 一个用户想要创建一个新的事务,会在控制器上调用这个方法

    def new  
      @penjualan = Penjualan.new  
      @penjualan.kode_transaksi = "J"+ DateTime.now.strftime("%d%m%Y%H%M%S")+@active_user.id.to_s  
      @customers = Customer.all(:limit => cookies[:limit], :order=>:kode_kustomer )  
      @barangs = Barang.all(:limit => cookies[:limit] )  
      respond_to do |format|  
        format.html # new.html.erb  
        format.json { render json: @penjualan }  
      end     
    end  
    

    http://pastebin.com/Lmp7hncn 第 648 行的完整控制器

  2. 在“新”视图中,我使用 :disable_with 禁用了按钮,这样用户就不能点击两次提交按钮,从而防止用户发起双重 POST 请求

    .row  
      .span4  
        = f.submit 'Proses', :class=>"btn btn-large btn-primary", :disable_with => "Processing..."
    

    http://pastebin.com/7b9W68RY 第 97 行的完整视图

  3. 提交的请求将调用控制器上的“create”方法,与 #1 相同的控制器,此方法在 1 秒的差异上被调用两次。更奇怪的是,这个请求绕过了我在模型上定义的唯一性验证,它应该使第二个请求失败,因为它与第一个请求具有相同的 kode_transaksi

  4. 我的模型 (Penjualan) 属性 (kode_transaksi) 有唯一性约束

    class Penjualan < ActiveRecord::Base  
      attr_accessible :customer_id, :jatuh_tempo, :kode_transaksi, :no_sj, :tanggal_bayar, :tanggal_transaksi, :total,:total_diskon, :ongkos, :user_id, :status_pembayaran, :is_returned, :kartu_kredit, :kartu_debit  
      has_many :detil_penjualans  
      attr_accessible :cash_total, :kembali  
      belongs_to :user  
      belongs_to :customer  
    
      validates :kode_transaksi, :uniqueness =>{:message=>"Transaksi Sudah Terjadi"}  
    
      scoped_search :on => [:kode_transaksi, :tanggal_transaksi, :status_pembayaran, :tanggal_bayar, :jatuh_tempo, :total ]  
      scoped_search :in => :customer, :on => [:nama_kustomer, :kode_kustomer]  
      scoped_search :in => :user, :on => [:username]  
    end  
    
  5. 我的生产日志和案例的sn-p

    Started POST "/penjualans" for 192.168.1.104 at 2012-11-24 12:15:40 +0900   
    Processing by PenjualansController#create as HTML     
    Parameters: {.... too long, see below ....}  
    
    
    Started POST "/penjualans" for 192.168.1.104 at 2012-11-24 12:15:41 +0900   
    Processing by PenjualansController#create as HTML     
    Parameters: {..... too long, see below ....}   
    Redirected to url/penjualans/17403   
    Completed 302 Found in 378ms (ActiveRecord: 246.0ms)   
    Redirected to url/penjualans/17404   
    Completed 302 Found in 367ms (ActiveRecord: 233.8ms)
    

日志片段http://pastebin.com/3tpua9gi

  1. 这种情况在我的数据库中创建了一个重复条目,导致问题

我真的对这种行为感到困惑,我束手无策。任何帮助将不胜感激。

【问题讨论】:

  • 在旧版本的 :disable_with 中,如果您使用 :disable_with,Rails 生成的 html 中的“onclick”中有一个“this.form.submit”。不知道它是否仍然如此,或者它是否可以解释你的问题。如果用户使用 提交而不是单击按钮,我认为表单无论如何都会提交(虽然它没有被禁用)。如果他们使用 或单击提交按钮会有区别吗?

标签: ruby-on-rails ruby post


【解决方案1】:

为了快速解决问题,我建议您在模型之外向数据库添加唯一约束。

The rails docs suggest 唯一性验证应伴随数据库中的唯一性约束,以防止两个连接同时插入相同的唯一值时出现问题。

除此之外,用户快速连续双击表单是否存在问题?也许禁用表单无法正常工作,因此允许用户单击两次?

是每天同一时间还是只在特定时间?

【讨论】:

  • 嗯,我现在将约束添加到数据库中。它只发生过两次,今天和昨天,时间看似随机。系统已经投产两个月了,没有做任何修改,这种情况真的让我很困惑。
  • 是的,真的很奇怪.. 约束应该防止问题再次出现,但可能会密切关注日志以查看它是否再次发生。也可能是某个随机机会将一个请求分派到您的 Web 服务器中的两个工作进程。不知道这是怎么发生的,但谁知道呢..
  • Nginx 在作为反向代理工作时(例如在 Thins 或 Unicorns 前面)有时会在后端发生某些错误时重新调度请求。违反 RFC 2616,它甚至对绝对不打算重新分派的 POST 和 PUT 请求执行此操作……因此,您确实必须确保您的数据库约束是合理的。
  • 明天会回到这里并发布结果,感谢您回复@Tigraine
  • 添加唯一约束解决了这个问题,虽然看到日志,仍然发出双重 POST 请求。可能是由于 Holger 刚刚解释的原因
【解决方案2】:

这个问题是由 Rails 中基于模型的唯一性约束的实现方式引起的。基本上,它们的工作方式是询问数据库是否存在针对给定唯一性约束的任何现有行,如果是这种情况,则拒绝创建对象。

但是,鉴于常用的事务隔离级别(通常是可重复读取),您可以有重叠的事务,它们都成功地检查了约束,然后在彼此不知道的情况下插入它们的对象。

这是因为要实现真正的唯一性,您必须使用UNIQUE 索引在数据库中定义您的约束。这比在模型中定义约束更重要,因为只有数据库能够通过在多线程操作期间实际插入/更新行来检查约束来确保实际唯一性。

您仍然希望在 Ruby 中额外定义约束的唯一原因是它的错误消息更加友好,因此您可以处理常见情况。

如果遇到数据库约束而不是 Rails 约束,则在调用 save 时只会返回 false,除了数据库约束失败之外,没有太多信息是什么问题。然而,好处是您可以保证之后仍然拥有一致的数据库。

【讨论】:

  • 希望有一个问题可以解决,如果您将创建包装在事务中并使用某种数据库锁,会解决问题吗?不是说它是一个更好的解决方案,只是想知道它是否有效。
  • @244an 这或多或少是事务的作用。虽然您可以手动完成,但根据您的需求(基本上是一致性和性能之间的权衡)选择正确的事务隔离级别并定义所需的数据约束可能更容易(并且性能更高)。
猜你喜欢
  • 1970-01-01
  • 2018-11-13
  • 2017-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-14
  • 2018-08-23
相关资源
最近更新 更多