You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Rails应用中点击一次下载按钮,download_file方法被调用两次?

解决Rails下载方法被重复调用导致积分重复扣减的问题

可能的原因及解决办法:

  • 浏览器预取/预加载触发重复请求
    部分现代浏览器会自动预取看起来是下载或导航的GET链接,导致请求被触发两次。可以给下载链接添加属性阻止预取:

    <%= link_to "Download", download_file_path, class: "download-button", rel: "noopener noreferrer nofollow", data: { no_prefetch: true } %>
    
  • 改用POST请求替代GET
    GET请求天生容易被缓存、预取,换成POST能从根源避免浏览器主动触发重复请求:

    1. 修改路由:
      # routes.rb
      post 'download_file', to: 'dashboard#download_file', as: 'download_file'
      
    2. 修改前端链接(用button_to或给link_to加method属性):
      <%= button_to "Download", download_file_path, class: "download-button", method: :post %>
      
      或
      <%= link_to "Download", download_file_path, class: "download-button", method: :post %>
      
      注:使用link_to的method属性需要确保项目中引入了Rails UJS(application.js里有//= require rails-ujs)
  • 添加幂等性校验,确保操作只执行一次
    即使请求被重复触发,也要通过数据库层面的控制保证积分只扣减一次,推荐使用乐观锁:

    1. 给accounts表添加lock_version字段(整数类型,默认值0):
      rails generate migration AddLockVersionToAccounts lock_version:integer
      rails db:migrate
      
    2. 修改控制器代码,用事务+乐观锁包裹扣积分逻辑:
      # dashboard_controller.rb
      def download_file
        puts "download_file method called"
        current_account.with_lock do
          if current_account.credit < 1
            redirect_to root_path, notice: "You don't have enough credits to download this file."
            return
          end
          puts current_account.credit
          current_account.credit -= 1
          current_account.save!
        end
        file = File.open("#{Rails.root}/test_file.zip", "r")
        self.response.headers["Content-Type"] = "application/zip"
        self.response.headers["Content-Disposition"] = "attachment; filename='test_file.zip'"
        self.response_body = file
      end
      

    这样即使两次请求同时到达,只有一个能成功提交事务,另一个会因乐观锁冲突失败,避免重复扣积分。

  • 检查前端是否重复绑定事件
    排查页面JS代码,确认是否不小心给下载按钮绑定了两次click事件。比如避免重复使用$('.download-button').on('click', ...),可以先解绑再绑定:

    $('.download-button').off('click').on('click', function(e) {
      // 你的逻辑
    });
    

内容的提问来源于stack exchange,提问作者Ali Ove

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 21:01:37