【问题标题】:Rails remote form parameter not being passed to controllerRails远程表单参数未传递给控制器
【发布时间】:2017-10-24 19:15:24
【问题描述】:

我的 Rails 应用程序中有一个表单,用户只需输入一个数字,然后使用 ajax 提交表单。如果所有参数都存在,则会在我的游戏控制器中创建一个新游戏。我有两个 hidden_​​field_tags,它们的参数很好地传递给控制器​​,但最重要的参数,即从用户输入获得的参数,似乎没有传递给控制器​​。

我的表格:

<%= form_for @game, :url => {:controller => "games", :action => "create" }, :html => { :role => 'form' }, remote: true, method: :post do |f| %>
  <div class="row">
    <div class="col-md-4 col-md-offset-4">
      <div class="input-group">
        <%= f.text_field :user_stake, class: 'form-control' %>
        <span class="input-group-btn">
          <%= f.submit 'Go', :html => { :type => "button" }, class: "btn btn-default" %>
        </span>
      </div>
    </div>
  </div>

    <%= hidden_field_tag 'user_id', current_user.id  %>
    <%= hidden_field_tag 'jackpot_id', @jackpot.id  %>

<% end %>

控制器:

 before_action :game_params, only: [:create]

  def create
    @game = Game.new(game_params)
    if @game.save
      @jackpot = Jackpot.find(params[:jackpot_id])
      ActionCable.server.broadcast 'jackpot_channel',
                                        users: Jackpot.where(id: @jackpot.id),
                                        pot: @jackpot,
                                        user: User.find(@game.user_id),
                                        game: @game,
                                        stake: @game.user_stake.to_s
    else
      ActionCable.server.broadcast 'jackpot_channel',
                                        error: @game.errors.full_messages
    end
  end

  def new
    @game = Game.new
  end

  private
    def game_params
      params.permit(:user_stake, :user_id, :jackpot_id)
    end

无论在@game 中输入什么,都会以 0.0 的 user_stake 保存,我已在迁移中将其设置为默认值。我不知道我在这里做错了什么。有任何想法吗?谢谢!

【问题讨论】:

  • Rails 服务器中是否有提示提交表单?
  • 删除before_action :game_params, only: [:create]。除了浪费时间/内存之外,它绝对没有任何作用。
  • 什么是 DB 列类型?
  • @max 是小数

标签: ruby-on-rails model-view-controller parameters actioncable


【解决方案1】:

您没有正确嵌套输入:

<%= form_for @game, html: { role: 'form' }, remote: true %>  
  <%= f.text_field :user_stake, class: 'form-control' %>
  <%= hidden_field_tag 'user_id', current_user.id  %>
  <%= hidden_field_tag 'jackpot_id', @jackpot.id  %> 
  # ..
<% end %>

这将给出以下参数哈希:

{
   game: {
     user_stake: 1.2
   },
   user_id: 3,
   jackpot_id: 4
}

如果你通过白名单发送,你会得到:

{
   user_id: 3,
   jackpot_id: 4
}

解决方案是简单地嵌套输入:

<%= form_for @game, html: { role: 'form' }, remote: true %>  
  <%= f.text_field :user_stake, class: 'form-control' %>
  <%= f.hidden_field_tag 'user_id', current_user.id  %>
  <%= f.hidden_field_tag 'jackpot_id', @jackpot.id  %> 
  # ..
<% end %>

并将它们正确列入白名单:

private
  def game_params
    params.require(:game)
          .permit(:user_stake, :user_id, :jackpot_id)
  end

但这里有一个巨大的警告标志 - 永远不要通过参数传递当前用户 ID,因为它使恶意用户很容易只使用网络检查器进行黑客攻击。而是直接使用会话中的值。

您无法伪造,除非使用用户密码或知道应用程序机密。

此外,如果游戏属于累积奖金,我会将其设置为 nested resource 并将 id 放在路径中,因为这会创建一个 RESTful 结构,清楚地表明您正在向父资源添加子资源 - 而不是隐藏请求正文中的重要信息。

# routes.rb
resources :jackpots do
  resources :games, shallow: true
end

class GamesController
  before_action :set_jackpot, only: [:new, :create, :index]

  # GET /jackpots/:jackpot_id/games/new
  def new
    @game = @jackpot.games.new
  end

  # POST /jackpots/:jackpot_id/games
  def create
    @game = @jackpot.games.new(game_params) do |g|
      g.user = current_user
    end

    if @game.save
      # ...
    else
      # ...
    end
  end

  # GET /jackpots/:jackpot_id/games
  def index
    @games = @jackpot.games
  end

  private 

    def set_jackpot
      @jackpot = Jackpot.includes(:games)
                        .find(params[:jackpot_id])
    end

    def game_params
      params.require(:game).permit(:user_stake)
    end
end

<%= form_for [@jackpot, @game], remote: true, html: { role: 'Form' } do |f| %>
  <div class="row">
    <div class="col-md-4 col-md-offset-4">
      <div class="input-group">
        <%= f.number_field :user_stake, class: 'form-control' %>
        <span class="input-group-btn">
          <%= f.submit 'Go', :html => { :type => "button" }, class: "btn btn-default" %>
        </span>
      </div>
    </div>
  </div>
<% end %>

注意如何不需要隐藏输入。

【讨论】:

  • 谢谢,这是一个非常全面且有用的答案。我按照您的建议做了,现在一切似乎都运行良好。只有一个问题:我想知道我是否应该像你说的那样保留路线,因为 Game 不属于 Jackpot,而是我已经建立了一个多对多关联,其中 User has_many :jackpots,通过 => 游戏。我应该像你说的那样保留路线还是改变它们?谢谢
  • 那么在这种情况下,您确实在累积奖金和游戏之间有一个 belongs_to 关联。
  • 一个游戏属于一个头奖,一个头奖有很多游戏。所以是的,嵌套它仍然有意义。
【解决方案2】:

您可能需要检查您的服务器日志以查看发布到控制器的 create 方法的内容。我怀疑游戏参数被封装在game 哈希中。要访问它,我建议将 game_params 更改为需要游戏:

def game_params
  params.require(:game).permit(:user_stake, :user_id, :jackpot_id)
end

【讨论】:

  • 感谢您的回答。提交表单后,我在日志中看到: INSERT INTO "games" ("user_id", "jackpot_id", "created_at", "updated_at") 。 user_stake 不存在。为了清楚起见,我试图通过 user_stake 而不是游戏。游戏是我的榜样
  • 是的...但是您的 game_params 可能会过滤掉您的参数。我建议检查game_paramsparams 的结果。这将帮助您诊断数据被过滤掉的位置。将 byebug 放在 create 方法的顶部可以帮助您检查这些变量。
猜你喜欢
  • 2016-05-11
  • 1970-01-01
  • 2011-06-05
  • 2017-10-22
  • 2019-01-06
  • 2020-05-09
  • 2012-11-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多