Understanding Rails Parameters

May 16, 2022   • rails

If you are building a dynamic web application, you will need to access the user-submitted data on the server. Maybe the user uploaded a photo, wrote a comment, posted a tweet, or clicked a link that includes the id of the article they want to read. How do you access this data on the back-end?

That’s where parameters enter into the picture. This article explains everything you need to know to understand Rails parameters.

There are two ways to get data from the front-end to the back-end.

  1. Query Strings
    • Include the data in the URL after the ?, e.g. blog.com/posts?id=3&user=ak
    • Data consists of key-value pairs separated by &
    • Typically submitted in a GET request, e.g. clicking a link
  2. Form Submission
    • Contains the data entered into a form by the user.
    • Typically posted in a POST request, e.g. submitting a form

The good thing is that Rails doesn’t care about the request type (GET or POST) when accessing the data on the server. It won’t distinguish between the information from query strings and the data from a form submission. It groups all the data into the params hash, which is available in the controller.

In reality, the params object is an instance of ActionController::Parameters class, that acts like a hash. So we can assume it’s a hash for all practical purposes.

Query Strings

The following route in the routes.rb file routes the /posts/sample URL to the sample method on the PostsController class.

get 'posts/sample', to: 'posts#sample'

Here’s the PostsController#sample method. To keep things simple, I am rendering plain text without using a view template.

class PostsController < ApplicationController
  def sample
    render plain: "#{params} \n\nid: #{params[:id]} \nuser: #{params[:user]}"

Now, when I go to the URL http://localhost:3000/posts/sample?id=3&user=ak, it renders:

{"id"=>"3", "user"=>"ak", "controller"=>"posts", "action"=>"sample"} 

id: 3 
user: ak

Note that the params hash also includes the controller and action names. Though you can access it in your controller, Rails recommends using the controller_name and action_name methods to access these values.

Form Submission

The following route in the routes.rb file routes the

  • posts/add URL to the add method on the PostsController class.
  • posts/upload URL to the build method on the PostsController class.
get 'posts/add', to: 'posts#add'
post 'posts/upload', to: 'posts#build'

Here’s the PostsController#add method, which renders the add.html.erb view. With the Rails conventions, it will render the add.html.erb view template.

class PostsController < ApplicationController
  def add

Let’s create a simple form in our add.html.erb template. When submitted, this form will make a POST request on the /posts/upload endpoint.

<form action="upload" method="post" data-turbo="false">
    <label for="name">Name:</label>
    <input type="text" id="name" name="user_name">

    <label for="mail">E-mail:</label>
    <input type="email" id="mail" name="user_email">

  <button type="submit">Submit</button>

To handle this Post request, let’s add the build method on the PostsController. Again, I am simply rendering the data as plain text, without the view to keep things simple. I have also disabled the Rails authenticity_token security measure to avoid adding unnecessary complexity to the example (don’t do this in a real application).

skip_before_action :verify_authenticity_token, only: :build

def build
  render plain: "#{params} \n\nname: #{params[:user_name]} \nemail: #{params[:user_email]}"

Now, when you enter some data in the form and click the Submit button, you are greeted with:

{"user_name"=>"Akshay", "user_email"=>"[email protected]", "controller"=>"posts", "action"=>"build"} 

name: Akshay 
email: [email protected]

Notice that the keys I am using on the params hash are the same values I used for the name attributes on the form. Rails extracts these values from the submitted data and adds them to the params hash.

Instead of sending the name values as keys directly, you can group them under a common key in the name attribute.

<form action="upload" method="post">
    <label for="name">Name:</label>
    <input type="text" id="name" name="client[user_name]">

    <label for="mail">E-mail:</label>
    <input type="email" id="mail" name="client[user_email]">

  <button type="submit">Submit</button>

Let’s modify the build method to access the nested parameters:

def build
    render plain: "#{params.to_json} \n\nname: #{params[:client][:user_name]} \nemail: #{params[:client][:user_email]}"

This prints the following output:

{"client":{"user_name":"Akshay","user_email":"[email protected]"},"controller":"posts","action":"build"} 

name: Akshay 
email: [email protected]

Notice that Rails has grouped the user_name and user_email under the client.

Sending JSON

For a rails API, you can send JSON data from the client. Rails will load it into the params hash, and you can access it in the controllers. For example, if you send the following JSON data:

{ "company": { "name": "acme", "address": "123 Carrot Street" } }

The params hash will be

{ :company => { "name" => "acme", "address" => "123 Carrot Street" } }

Dynamic Parameters

If your route URL contains a symbol, Rails will use that symbol name as the key in the params hash. In the following example, the route instructs Rails to map the URL parameter to title.

# routes.rb
get 'posts/lookup/:title', to: 'posts#lookup'

# posts_controller
def lookup
  render plain: "#{params} \n\ntitle: #{params[:title]}"

# browser output
{"controller"=>"posts", "action"=>"lookup", "title"=>"rails"} 

title: rails

Passing Parameters when Redirecting

When redirecting to another route, you can pass the parameters by passing them as hash parameters to the redirect_to method. For example,

redirect_to controller: 'articles', action: 'show', id: 3, user: 'ak'

The ArticlesController can access the passed values using the params hash as usual. However, an important thing to note is that you can’t redirect a POST request.

I hope that you have a pretty good understanding of how parameters work in Rails by now. To summarize what we’ve learned so far:

  • Rails doesn’t differentiate between the data that comes from a GET request (query string parameters) vs. the data that comes from a POST request (form submission), and
  • All the data is available in the params hash.

In the next post on strong parameters, we will look at a common problem in software, known as Mass Assignment Vulnerability, and how Rails helps us tackle it by providing strong parameters.