简体   繁体   中英

Rails remote form parameter not being passed to controller

I have a form in my rails app where a user just enters a number and the form is then submitted with ajax. A new game is created in my gamescontroller if all the parameters are present. I have two hidden_field_tags whose parameters pass to the controller very nicely but the most important parameter, the one gotten from user input, doesn't seem to pass to the controller.

My form:

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

Controller:

 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

No matter what is typed in the @game gets saved with a user_stake of 0.0, which I have set as the default in my migrations. I have no idea what I'm doing wrong here. Any ideas? Thanks!

You may want to inspect your server logs to see what is being posted to your controller's create method. I suspect the game Parameters are encapsulated in a game hash. To access it, I'd recommend changing game_params to require game:

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

You're not nesting the inputs properly:

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

This would give the following params hash:

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

If you send that through the whitelist you get:

{
   user_id: 3,
   jackpot_id: 4
}

On solution is to simply nest the inputs:

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

And whitelist them properly:

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

But there is a huge warning flag here - NEVER pass the current user id through the params as it makes it really easy for a malicous user to hack with nothing but the web inspector. Instead use the value directly from the session.

You can't fake that except with the users password or by knowing the application secret.

Also if a game belongs to a jackpot I would set it up as a nested resource and put the id in the path as this creates a RESTful structure that clearly shows that you are adding children to a parent resource - instead of hiding that vital information in the request body.

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

Note how there are no hidden inputs required.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM