Rails 远程表单参数未传递给控制器

Rails remote form parameter not being passed to controller

我的 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 中输入什么,都会使用 user_stake 0.0 保存,我已将其设置为迁移中的默认值。我不知道我在这里做错了什么。有任何想法吗?谢谢!

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

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

您没有正确嵌套输入:

<%= 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 结构,清楚地表明您正在添加 children 到 parent 资源 - 而不是将重要信息隐藏在请求正文中。

# 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 %>

注意不需要隐藏的输入。