More at rubyonrails.org:

Action Controller Overview

In this guide, you will learn how controllers work and how they fit into the request cycle in your application.

After reading this guide, you will know how to:

  • Follow the flow of a request through a controller.
  • Render HTTP responses as well as redirect responses.
  • Work with action callbacks to execute code during request processing.
  • Access and securely filter parameters passed to your controller.
  • Store data in the cookie, the session, and the flash.
  • Use the Request and Response Objects.

1. Action Controller Basics

Action Controller is the C in the Model View Controller (MVC) pattern. The router matches a controller to an incoming request, which is responsible for processing the request and generating the response.

For most conventional RESTful applications, the controller will receive the request, fetch or save data from a model, and render a view to create HTML output.

A controller sits between models and views. The controller makes model data available to the view, so that the view can display that data to the user. The controller also receives user input from the view and saves or updates model data accordingly.

1.1. Structure of a Controller

A controller is a Ruby class which inherits from ApplicationController and has methods just like any other class. Public methods in a controller are also known as actions, as they are responsible for rendering responses.

# app/controllers/products_controller.rb

class ProductsController < ApplicationController
  def index
  end
end

As per Rails conventions, controllers should define up to 7 conventional CRUD actions as demonstrated below. This convention is used by the Router DSL to configure resourceful routes to the controller. Other actions may be defined and routed to, but these are outside Rails conventions. Further details are available in the Routing guide.

# config/routes.rb

Rails.application.routes.draw do
  # A resourceful route to the `ProductsController`
  resources :products
end
# app/controllers/products_controller.rb

class ProductsController < ApplicationController
  # GET /products
  def index
    # Display all products
  end

  # GET /products/new
  def new
    # Render a form to create a new product
  end

  # POST /products
  def create
    # Handle the submission form rendered in `new`
    # and create the product in the database
  end

  # GET /products/:id
  def show
    # Show the product
  end

  # GET /products/:id/edit
  def edit
    # Render a form to edit the product
  end

  # PATCH /products/:id
  def update
    # Handle the submission form rendered in `edit`
    # and update the product in the database
  end

  # DELETE /product/:id
  def destroy
    # Delete the product
  end
end

Once the router matches an incoming request to a controller and action, Rails creates an instance of that controller class and calls the method with the same name as the action.

The new method in the above controller is an instance method, called on an instance of ProductsController. This should not be confused with the new class method used to instantiate objects (ProductsController.new).

1.2. Naming Conventions

Rails favors pluralizing the resource in the controller's name. For example, ProductsController is preferred over ProductController and SiteAdminsController over SiteAdminController or SitesAdminsController. However, the plural names are not strictly required — for example, ApplicationController.

Following this naming convention will allow you to use resourceful routes without needing to qualify each with additional options. The convention also makes named route helpers consistent throughout your application.

The controller naming convention is different from models. While plural names are preferred for controller names, the singular form is preferred for model names (e.g. Account vs. Accounts).

Controller actions should be public, as only public methods are callable as actions. Helper methods within controllers which are not intended to be actions should be declared private or protected.

Ensure that you do not override methods defined by ActionController::Base when creating actions. Accidentally redefining them could result in SystemStackError. If you limit your controllers to the 7 CRUD actions, this won't be an issue.

If you must use a reserved method as an action name, one workaround is to use a custom route to map the reserved method name to your non-reserved action method.

1.3. Rendering Responses

Consider the below route and controller.

# config/routes.rb

Rails.application.routes.draw do
  # A resourceful route to the `ProductsController`
  resources :products
end
class ProductsController < ApplicationController
  def index
  end
end

When a user navigates to /products, Rails will create an instance of ProductsController and call its index method. If the index method is empty, Rails will automatically render app/views/products/index.html.erb.

You can explicity define the template to render using the render method:

class ProductsController < ApplicationController
  def index
    # Renders `app/views/products/feed.html.erb`
    render "feed"
  end
end

Calling render does not return from the current scope. Statements after the method call will still be executed. Ensure you do not call render more than once in any given action, as it will raise an AbstractController::DoubleRenderError.

To enable rendering of additional formats, not just HTML, use a respond_to block. This will choose the correct template based on the Accept header in the HTTP request:

class ProductsController < ApplicationController
  def index
    respond_to do |format|
      format.html   # renders `app/views/products/index.html.erb`
      format.xml    # renders `app/views/products/index.xml.erb`
    end
  end
end

See the Layouts and Rendering guide for futher details on rendering responses.

In the index method, the controller would typically create an array of the Product model instances, and make it available as an instance variable called @products in the view:

def index
  @products = Product.all
end

All controllers inherit from ApplicationController, which in turn inherits from ActionController::Base. For API only applications ApplicationController inherits from ActionController::API.

1.4. Redirecting Requests

Instead of rendering a fully-formed response, such as an HTML document, you might want to redirect the user to a different path. An HTTP redirection status code can be used for this. Rails' redirect_to method uses 302 Found by default.

redirect_to photos_url

This will send a 302 HTTP response to the browser with photos_url in the Location header. The browser will then make a new GET request to the photos_url.

HTTP/1.1 302 Found
referrer-policy: strict-origin-when-cross-origin
location: http://localhost:3000/photos
content-type: text/html; charset=utf-8
cache-control: no-cache
connection: close
content-length: 0

It's worth being aware that redirect_to doesn't move execution to a different method within the same request. It sends an HTTP response and then the browser makes a new request to the redirected location which won't have any context from the previous request.

Alternatively, you can use redirect_back to return the user to the page they just came from. The location is pulled from the HTTP_REFERER header which is not guaranteed to be set by the browser, so you must provide a fallback_location.

redirect_back(fallback_location: root_path)
# or
redirect_back_or_to root_path

Similar to render, redirect_to and redirect_back do not automatically return from the current scope, instead they simply set the HTTP response. Statements occurring after them will still be executed.

Use the status: option to use a different HTTP response code. Both numeric and symbolic values are valid.

redirect_to photos_path, status: :see_other

When redirecting using 302 Found, the client may not always make a GET request to the Location. JavaScript's fetch method will make a GET request after redirection from a GET or POST request. But, if the initial request is another method, for example PUT, it will use the same method when requesting the redirected location. This is not a problem in Rails because all form submissions use POST, even when using Turbo.

When making requests in custom JavaScript, redirect using 303 See Other to ensure a GET request to the redirected location, or 307 Temporary Redirect to preserve the original HTTP verb.

1.5. Header-Only Responses

The head method can be used to send responses with only headers to the browser. This is usually in response to an HTTP HEAD request.

The head method accepts a number or symbol representing an HTTP status code.

head :bad_request
# or
head 400

This would produce the following HTTP response:

HTTP/1.1 400 Bad Request
connection: close
transfer-encoding: chunked
content-type: text/html; charset=utf-8
set-cookie: _blog_session=...snip...; path=/; HttpOnly
cache-control: no-cache

You can add additional HTTP headers if you wish.

head :created, location: photo_path(@photo)

Which would produce:

HTTP/1.1 201 Created
connection: close
transfer-encoding: chunked
location: /photos/1
content-type: text/html; charset=utf-8
set-cookie: _blog_session=...snip...; path=/; HttpOnly
cache-control: no-cache

2. Controller Callbacks

Controller callbacks are methods that are defined to automatically run before or after a controller action. Callbacks may be defined in the ApplicationController if they need to be run across the application, or within specific controllers only.

2.1. before_action

Callback methods registered via before_action run before a controller action. They may halt the request cycle meaning the controller action itself is never called.

A common use case for before_action is to ensure that a user is logged in:

class ApplicationController < ActionController::Base
  before_action :require_login

  private
    def require_login
      unless Current.user.present?
        redirect_to new_login_url # halts request cycle
      end
    end
end

When a before_action callback renders or redirects (like in the example above), the controller action is not run. If there are additional callbacks registered to run, they will be cancelled.

In this example, the before_action is defined in ApplicationController meaning it will be run before all actions across the applications. This also means the user will need to be logged into access the login page, which doesn't make sense.

In such cases, use skip_before_action to allow specified controller actions to skip a given before_action:

class SessionsController < ApplicationController
  skip_before_action :require_login, only: [:new, :create]
end

Now, the SessionsController's new and create actions will work without requiring the user to be logged in.

The :only option skips the callback only for the listed actions. There is also an :except option which works the other way. These options can be used when registering action callbacks to run them only for specific actions.

If you register the same action callback multiple times with different options, the last action callback definition will overwrite the previous ones.

2.2. after_action and around_action

You can also define action callbacks to run after a controller action has been executed with after_action, or to run both before and after with around_action.

The after_action callbacks are similar to before_action callbacks, but because the controller action has already been run they have access to the response data that's about to be sent to the client.

after_action callbacks are executed only after a successful controller action, and not if an exception is raised in the request cycle.

The around_action callbacks are useful when you want to execute code before and after a controller action, allowing you to encapsulate functionality that affects the action's execution. They are responsible for running their associated actions by yielding.

For example, imagine you want to monitor the performance of specific actions. You could use an around_action to measure how long each action takes to complete and log this information:

class ApplicationController < ActionController::Base
  around_action :measure_execution_time

  private
    def measure_execution_time
      start_time = Time.now
      yield  # This executes the action
      end_time = Time.now

      duration = end_time - start_time
      Rails.logger.info "Action #{action_name} from controller #{controller_name} took #{duration.round(2)} seconds to execute."
    end
end

Action callbacks receive controller_name and action_name as parameters you can use, as shown in the example above.

The around_action callback also wraps rendering. In the example above, view rendering will be included in the duration. The code after the yield in an around_action is run even when there is an exception in the associated action and there is an ensure block in the callback.

This is different from after_action callbacks where an exception in the action cancels any after_action callbacks.

2.3. Advanced Techniques to Register Callbacks

In addition to registering a method name using before_action, after_action, or around_action, there are two other ways to register callbacks.

2.3.1. Using a Block

A block can be supplied to the *_action methods. It receives the controller as an argument. The require_login action callback from above could be rewritten to use a block:

class ApplicationController < ActionController::Base
  before_action do |controller|
    unless Current.user.present?
      redirect_to new_login_url
    end
  end
end

Specifically for around_action, the block also yields in the action:

around_action { |_controller, action| time(&action) }

2.3.2. Using an Object

An object, usually a class, can be used to register callbacks. The object must implement a method with the same name as the action callback.

For the before_action callback, the class must implement a before method, and so on. Also, the around method must yield to execute the action.

This can be useful in cases that are more complex. As an example, you could rewrite the around_action callback to measure execution time with a class:

class ApplicationController < ActionController::Base
  around_action ActionDurationCallback
end

class ActionDurationCallback
  def self.around(controller)
    start_time = Time.now
    yield # This executes the action
    end_time = Time.now

    duration = end_time - start_time
    Rails.logger.info "Action #{controller.action_name} from controller #{controller.controller_name} took #{duration.round(2)} seconds to execute."
  end
end

In the above example, the ActionDurationCallback's method is not run in the scope of the controller but gets controller as an argument.

3. Request Parameters

Data sent by the incoming request is available in your controller using the params method which returns an ActionController::Parameters object.

There are two types of parameter data:

  • Query string parameters which are sent as part of the URL (for example, after the ? in http://example.com/accounts?filter=free).
  • Request body parameters in non-GET requests, usually from an HTML form.

Rails does not make a distinction between query string parameters and request body parameters — both are available in the params object in your controller. For example:

class ProductsController < ApplicationController
  # This action receives query string parameters from an HTTP GET request
  # at the URL "/products?filter=books"
  def index
    if params[:filter] == "books"
      @products = Product.books
    else
      @products = Product.all
    end
  end

  # This action receives parameters from a POST request to "/products" URL with
  # form data in the request body.
  def create
    @product = Product.new(params[:product])
    if @product.save
      redirect_to @product
    else
      render "new", status: :unprocessable_content
    end
  end
end

ActionController::Parameters does not inherit from Hash, but it is mostly similar in behavior. Unlike a Hash, symbolic and string keys, such as :foo and "foo", are considered to be the same.

3.1. Parameter Value Types

Values in an ActionController::Parameters object must be a permitted scalar value defined in ActionController::Parameters::PERMITTED_SCALAR_TYPES.

ActionController::Parameters::PERMITTED_SCALAR_TYPES
# => [String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, StringIO, IO, ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile]

The object can contain nested hashes and arrays, but values within those must also contain a permitted scalar.

To send an array of values, append an empty pair of square brackets [] to the key name:

GET /users?ids[]=1&ids[]=2&ids[]=3

The actual URL in this example will be encoded as /users?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3 as the [ and ] characters are not allowed in URLs. Most of the time you don't have to worry about this because the browser will encode it for you, and Rails will decode it automatically, but if you ever find yourself having to send those requests to the server manually you should keep this in mind.

The value of params[:ids] will be the array ["1", "2", "3"]. Parameter values are always strings, Rails does not attempt to guess or cast the type.

Values such as [nil] or [nil, nil, ...] in params are replaced with [] for security reasons by default. See Security Guide for more information.

To send a hash, you include the key name inside the brackets:

<form accept-charset="UTF-8" action="/users" method="post">
  <input type="text" name="user[name]" value="Acme" />
  <input type="text" name="user[phone]" value="12345" />
  <input type="text" name="user[address][postcode]" value="12345" />
  <input type="text" name="user[address][city]" value="Carrot City" />
</form>

When this form is submitted, the value of params[:user] will be:

{
  "name" => "Acme",
  "phone" => "12345",
  "address" => {
    "postcode" => "12345",
    "city" => "Carrot City"
  }
}

Note the nested hash in params[:user][:address].

Rails provides helpers to construct HTML forms that adhere to Rails conventions. Refer to the Form Helpers guide for further information.

3.2. Composite Key Parameters

Composite key parameters contain multiple values in one parameter separated by a delimiter (such as an underscore). Therefore, you will need to extract each value so that you can pass them to Active Record. You can use the extract_value method to do that.

Consider the below controller and route:

class ProductsController < ApplicationController
  def show
    # Extract the composite ID value from URL parameters.
    id = params.extract_value(:id)
    @product = Product.find(id)
  end
end
get "/products/:id", to: "products#show"

When a user requests the URL /products/4_2, the controller will extract the composite key value ["4", "2"] and pass it to Product.find. The extract_value method may be used to extract arrays out of any delimited parameters.

3.3. JSON Parameters

If your application exposes an API, you will likely accept parameters in JSON format. If the content-type header of your request is set to application/json, Rails will automatically load your parameters into the params hash, which you can access as you would normally.

So for example, if you are sending this JSON content:

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

Your controller will receive:

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

3.3.1. Parameter Wrapping

Rails will automatically wrap parameters within a key denoting the corresponding resource, and also add the controller name to JSON parameters.

For example, consider the below JSON object:

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

If we send the above data to create action of the UsersController, the JSON data will be wrapped within the :user key as:

{
  controller: "users",
  action: "create",
  name: "acme",
  address: "123 Carrot Street",
  user: {
    name: "acme", address: "123 Carrot Street"
  }
}

Rails adds a clone of the parameters to the hash within the key corresponding to the resource's name. As a result, both the original version of the parameters and the "wrapped" version of the parameters will exist in the params object.

While the action and controller names are available in the params object, it is recommended to use the methods controller_name and action_name instead to access these values.

Parameter wrapping is enabled by default, but can be disabled using a configuration option:

# config/application.rb

config.action_controller.wrap_parameters_by_default = false

You can also customize the name of the key or specific parameters you want to wrap, see the API documentation for more.

3.4. Routing Parameters

Parameters specified as part of a route declaration in the routes.rb file are also made available in the params hash. For example, we can add a route that captures the :category parameter for a product:

get "/products/:category", to: "products#index", foo: "bar"

When a user navigates to /products/electronics URL, params[:category] will be set to "electronics". When this route is used, params[:foo] will also be set to "bar", as if it were passed in the query string.

Any other parameters defined by the route declaration, such as :id, will also be available.

3.5. Global Default Parameters

You can set global default parameters when generating URLs by defining a default_url_options method in your controller.

class ApplicationController < ActionController::Base
  def default_url_options
    { locale: I18n.locale }
  end
end

The specified defaults will be used as a starting point when generating URLs. They can be overridden by the options passed to url_for or any path helper such as products_path. The above example will automatically add the locale to every URL.

products_path # => "/products?locale=en"

You can still override this default if needed:

products_path(locale: :fr) # => "/products?locale=fr"

All Rails path helpers call url_for under the hood.

If you define default_url_options in ApplicationController, as in the example above, these defaults will be used for all URL generation. The method can also be defined in a specific controller, in which case it only applies to URLs generated for that controller.

In a given request, the method is not actually called for every single generated URL. For performance reasons the returned hash is cached per request.

3.6. Securing Submitted Parameters

Action Controller Strong Parameters ensures parameters cannot be used in Active Model mass assignments until they have been explicitly permitted.

This requires you to specify the allowed attributes for any given model and declare them in the controller. This is a security practice to prevent users from accidentally or maliciously updating sensitive model attributes.

Consider the below controller and action:

class PeopleController < ActionController::Base
  def create
    @person = Person.create(params[:person])
    # ...
  end
end

We create a Person record using the parameters passed in the :person key, without explicitly defining which parameters are permitted. A Person may have a boolean attribute which specifies whether or not they're an admin. A malicious user could modify the HTML form to send this value and make themselves an admin without permission. As such, all parameters used in mass assignment must be explicitly allowed, and the above example will raise a ActiveModel::ForbiddenAttributesError.

3.6.1. Permitting Parameter Attributes

Each attribute must be manually permitted using its key. Nested hashes and arrays within attributes are allowed, and depending on the method used, nested keys may also need to be specified.

The expect, permit, and require methods are commonly used to specify permitted attributes.

3.6.1.1. expect

The expect method is the safest and most explicit way to permit parameters. If the request doesn't contain all expected parameters, ActionController::ParameterMissing will be raised and a 400 Bad Request HTTP response will be returned.

class UsersController < ApplicationController
  # ...

  def update
    id = params.expect(:id)
  end

  # ...
end

When handling Rails forms, use expect to ensure that the root key is present and define the permitted attributes.

class UsersController < ApplicationController
  # ...
  private
    def user_params
      params.expect(user: [:username, :password])
    end
end

expect is strict with types. Supplying a single key will return a scalar, and multiple keys will return an array of those values.

params = ActionController::Parameters.new(name: "John Doe")
params.expect(:name)
# => "John Doe"

params = ActionController::Parameters.new(name: "John Doe", title: "Mr")
params.expect(:name, :title)
# => ["John Doe", "Mr"]

Nested hashes and arrays must be specified, including any nested keys, or they will be filtered out.

params = ActionController::Parameters.new(user: { name: "John Doe" })
params.expect(:user)
# => param is missing or the value is empty or invalid: user (ActionController::ParameterMissing)
params.expect(user: [:name])
# => #<ActionController::Parameters {"name" => "John Doe"} permitted: true>

params = ActionController::Parameters.new(ids: ["1", "2", "3"])
params.expect(:ids)
# => param is missing or the value is empty or invalid: ids (ActionController::ParameterMissing)
params.expect(ids: [])
# => ["1", "2", "3"]

params = ActionController::Parameters.new(
  users: [{ name: "John Doe" }, { name: "Jane Doe" }]
)
params.expect(users: [])
# => param is missing or the value is empty or invalid: users (ActionController::ParameterMissing)
params.expect(users: [:name])
# => param is missing or the value is empty or invalid: users (ActionController::ParameterMissing)
params.expect(users: [[:name]])
# => [#<ActionController::Parameters {"name" => "John Doe"} permitted: true>, #<ActionController::Parameters {"name" => "Jane Doe"} permitted: true>]

Permit all attributes under a key using:

params.expect(user: {})

This does not check for types or any nested types. All contained attributes are permitted, which somewhat bypasses the security aspects of strong parameters.

Extreme care should be taken when calling expect with an empty hash, as it will allow all current and future model attributes to be mass-assigned.

3.6.1.2. permit and require

permit returns a new ActionController::Parameters object containing only the permitted attributes. Disallowed attributes are filtered out, and no error is raised.

params = ActionController::Parameters.new(user: { id: 1, admin: "true" })
params.permit(:user)
# => #<ActionController::Parameters {} permitted: true>
params.permit(user: [:id])
# => #<ActionController::Parameters {"user" => #<ActionController::Parameters {"id" => 1} permitted: true>} permitted: true>
params.permit(user: [:id, :admin])
# => #<ActionController::Parameters {"user" => #<ActionController::Parameters {"id" => 1, "admin" => "true"} permitted: true>} permitted: true>

All values under a key can be permitted using {}:

params = ActionController::Parameters.new(user: { id: 1, admin: "true" })
params.permit(user: {})
# => #<ActionController::Parameters {"user" => #<ActionController::Parameters {"id" => 1, "admin" => "true"} permitted: true>} permitted: true>

Exercise caution when calling permit with an empty hash, as it will allow all current and future model attributes to be mass-assigned.

permit is commonly chained with require to permit a set of attributes keyed by a resource name. require accepts a single key or an array of keys. It returns the associated values if found, or raises ActionController::ParameterMissing if any supplied keys are missing.

params = ActionController::Parameters.new(user: { id: 1, admin: "true" })
params.require(:user)
# => #<ActionController::Parameters {"id" => 1, "admin" => "true"} permitted: false>

As demonstrated above, the object returned by require(:user) has not yet been permitted. As such, require is often chained with permit to return a set of permitted attributes:

params = ActionController::Parameters.new(user: { id: 1, admin: "true" })
params.require(:user).permit(:id, :admin)
# => #<ActionController::Parameters {"id" => 1, "admin" => "true"} permitted: true>

Note that expect is the recommended technique to permit attributes, especially when working with nested objects, as it is more explicit about value types, and hence provides additional safety.

3.6.1.3. permit!

The permit! method sets the permitted attribute on an ActionController::Parameters object to true. It does no filtering or value checking whatsoever.

params = ActionController::Parameters.new(id: 1, admin: "true")
# => #<ActionController::Parameters {"id"=>1, "admin"=>"true"} permitted: false>
params.permit!
# => #<ActionController::Parameters {"id"=>1, "admin"=>"true"} permitted: true>

Since permit! does not check any values, it bypasses all security benefits provided by strong parameters. Use with extreme caution.

Further details on advanced parameter filtering are available in the API docs.

4. Cookies

A cookie (also known as an HTTP cookie or a web cookie) is a small piece of data from the server that is saved in the user's browser. The browser may store cookies, create new cookies, modify existing ones, and send them back to the server with later requests. Cookies persist data across web requests and therefore enable web applications to remember user preferences.

Rails provides access to cookies in a controller via the cookies method, which returns an instance of ActionDispatch::Cookies. ActionDispatch::Cookies is a key-value store and is similar to a Ruby Hash.

class PreferencesController < ApplicationController
  def new
    # Read data from a cookie
    @preferences = cookies[:preferences]
  end

  def create
    # Write data to a cookie
    cookies[:preferences] = params.expect(preferences: {})
  end

  def destroy
    # Delete a key from a cookie
    cookies.delete(:preferences)
  end
end

Setting a key to nil will not delete the cookie. You need to use cookies.delete(:key).

Cookies have a default lifetime of Session, so they will be deleted when the user closes their browser.

Create a permanent cookie that expires at a specific time by passing a hash with the :expires option:

cookies[:remember_me] = { value: "true", expires: 1.month }

Rails also provides a permanent cookie jar that automatically sets the expiration date to 20 years from the time of creation:

cookies.permanent[:locale] = "fr"

4.1. Encrypted and Signed Cookies

Since cookies are stored on the client browser, they can be susceptible to tampering and are not considered secure for storing sensitive data. Rails provides signed and encrypted cookie jars for storing sensitive data.

The signed cookie jar appends a cryptographic signature on the cookie data to protect its integrity. The data can be read by a user, but cannot be tampered with as it is cryptographically signed.

cookes.signed[:preferences] = @user.preferences.to_h

The encrypted cookie jar encrypts the data in addition to signing it, so that it cannot be read by the user nor tampered with.

cookes.encrypted[:remember_token] = @user.remember_token

Refer to the API documentation for more details.

These special cookie jars use a serializer to serialize the cookie values into strings and deserialize them into Ruby objects when read back. The default serializer for new applications is :json.

You can specify the serializer via config.action_dispatch.cookies_serializer.

Be aware that JSON has limited support serializing Ruby objects such as Date, Time, and Symbol. These will be serialized and deserialized into Strings.

If you need to store these or more complex objects, you may need to manually convert their values when reading them in subsequent requests.

5. Session

Rails provides a session object which is used to store data relevant to the current user session. For example, data related to user authentication may be stored in the session object.

Session data is stored in an encrypted cookie by default, but other stores may be configured.

5.1. Working with the Session

The session is available in the controller and the view via the session method which returns an instance of ActionDispatch::Http::Session. This object is a key-value store and values can be accessed and set in the same way as a Ruby Hash.

class SessionsController < ActionController::Base
  # Read the session to redirect the user back to their initial location
  # after a log in, or redirect them to the root path if no location is set.
  def create
    # ...

    redirect_to session[:initial_location] || root_path
  end
end

To store data the session, assign a value to a key as you would in a Ruby Hash.

class ProductsController < ApplicationController
  def create
    # ...

    # Handle logged out user
    session[:initial_location] = request.referer
    redirect_to login_path
  end
end

To remove something from the session, delete the key:

class ProductsController < ApplicationController
  def new
    session.delete(:initial_location)
  end
end

Delete all data and create a new session object using reset_session. It is recommended to use reset_session before logging in to avoid session fixation attacks.

Sessions are lazily loaded. If you don't access sessions in your action's code, they will not be loaded. Hence, you will never need to disable sessions - not accessing them will do the job.

5.2. Session Stores

The storage mechanism for session data can be configured. By default, data is stored in an encrypted cookie, but other stores are available.

All sessions have a unique ID representing the session object. Regardless of the chosen store, this session ID is always stored in a cookie. The session data can be stored using one of the following storage mechanisms:

For most session stores, Rails uses the unique session ID in the cookie to read session data from your chosen store. Rails does not allow you to pass the session ID in the URL as this is less secure.

5.2.1. CookieStore

The CookieStore is the default and recommended session store. It stores all session data, including the session ID, in an encrypted cookie. The CookieStore is lightweight and does not require any configuration to use in a new application.

Cookies are limited 4 kB of data, and the cookie store is bound by this limit. While this is lesser than the other storage options, it is usually enough. Storing large amounts of data in the session is discouraged. You should especially avoid storing complex objects (such as model instances) in the session.

5.2.2. CacheStore

You can use the CacheStore if your sessions don't store critical data or don't need to be around for long periods. This will store sessions using the cache implementation you have configured for your application. The advantage is that you can use your existing cache infrastructure for storing sessions without requiring any additional setup or administration. The downside is that the session storage will be temporary and data could disappear at any time.

Read more about session storage in the Security Guide.

5.3. Configuring the Session

Some aspects of the session can be configured.

Set the session store using:

# config/initializers/sessions.rb

Rails.application.config.session_store :cache_store

When using the cookie store, Rails automatically sets the name of the cookie. However, this can also be configured:

# config/initializers/sessions.rb

Rails.application.config.session_store :cookie_store, key: "_your_app_session"

Be sure to restart your server when you modify an initializer file.

You can also pass a :domain key and specify the domain name for the cookie:

Rails.application.config.session_store :cookie_store, key: "_your_app_session", domain: ".example.com"

See config.session_store in the configuration guide for more information.

Signed and encrypted cookies, including the session when using the cookie store, are signed using the secret_key_base generated for all new Rails applications. It is usually stored in the encrypted credentials file: config/credentials.yml.enc. Changing the secret_key_base will render all signed and encrypted cookies unreadable. Refer to the security guide for further information.

5.4. The Flash

The flash provides a way to pass temporary data between controller actions invoked in successive HTTP requests.

Anything you place in the flash will be available in the very next request, and then cleared.

The flash is typically used for setting messages such as notices and alerts in a controller action, before redirecting to an action that displays the message.

The flash is accessed via the flash method which returns an instance of ActionDispatch::Flash::FlashHash. Similar to the session, the flash values are stored as key-value pairs exactly like a Ruby Hash.

Consider the below example where the controller sets a flash message after a product is created, which will be available to display during the next request after the user is redirected.

class ProductsController < ApplicationController
  def create
    # ...

    flash[:notice] = "Your product was created!"
    redirect_to products_path, status: :see_other
  end
end

You may use different keys to assign different message types:

flash[:notice]  = "Your product was created!"
flash[:alert]   = "Sorry, something when wrong while creating your product."
flash[:warning] = "Your product was created, but there were some problems."

Set a flash message when calling redirect_to by including it as a parameter:

# Equivalent to setting flash[:notice]
redirect_to root_url, notice: "Your product was created!"

# Equivalent to setting flash[:alert]
redirect_to root_url, alert: "Sorry, something when wrong when creating your product."

Only notice: and alert: options may be used at the top-level. Storing messages under other keys needs the flash: option:

# Equivalent to setting flash[:warning]
redirect_to root_url, flash: { warning: "Your product was created, but there were some problems." }

The flash does not render anything in the UI — it is a short-term data storage mechanism. Reading flash data and rendering the appropriate HTML is left up the the developer.

5.4.1. Displaying Flash Messages

It is recommend to add code to render flash messages in your application layout, so messages are automatically rendered on every page without additional steps.

Iterate through all the keys and values to render all set messages, and then you can use CSS to style the different types of messages based on their type:

<%# app/views/layouts/application.html.erb %>

<html>
  <%# ... %>
  <body>
    <% flash.each do |type, message| -%>
      <%= tag.div class: class_names("flash", type) do %>
        <p><%= message %></p>
      <% end %>
    <% end -%>

    <%# ... %>
    <%= yield %>
  </body>
</html>

5.4.2. flash.keep and flash.now

flash.keep is used to carry over the flash value through to an additional request. This is useful when there are multiple redirects.

For example, assume that the root_url routes to the index action in the controller below, and all requests here are redirected to UsersController#index.

If an action sets the flash and redirects to MainController#index, those flash values will be lost during the next redirect.

Use flash.keep to persist the values in the flash for one more request.

class MainController < ApplicationController
  def index
    # Persists all flash values.
    flash.keep

    # Persists only the `:notice` value.
    flash.keep(:notice)

    # ...
  end
end

By default, setting flash value will make them available to the next request. flash.now is used to make the flash values available in the same request.

In the below example, when the create action fails to save a resource, the new template is rendered.

Since there is no redirection, the response will not immediately trigger another HTTP request. Use flash.now to display a message using the flash in this case. This will make the message available in only the current request.

class ProductsController < ApplicationController
  def create
    @product = Product.new(product_params)
    if @product.save
      # ...
    else
      flash.now[:error] = "The product could not be saved"
      render "new", status: :unprocessable_content
    end
  end
end

6. The Request and Response Objects

Every controller has two methods, request and response, which can be used to access the request and response objects associated with the current request cycle.

The request method returns an instance of ActionDispatch::Request. The response method returns an instance of ActionDispatch::Response.

6.1. The request Object

The request object contains useful information about the request coming in from the client. This section describes the purpose of some of the properties of the request object.

The full list of the available methods can be viewed in the Rails API documentation and Rack documentation.

Property of request Purpose
host The hostname used for this request.
domain(n=2) The hostname's first n segments, starting from the right (the TLD).
format The content type requested by the client.
method The HTTP method used for the request.
get?, post?, patch?, put?, delete?, head?, query? Returns true if the HTTP method is GET/POST/PATCH/PUT/DELETE/HEAD/QUERY.
safe_method?, unsafe_method? Returns true if the HTTP method is safe (GET, HEAD, QUERY, OPTIONS, TRACE) / unsafe.
headers Returns a hash containing the headers associated with the request.
port The port number (integer) used for the request.
protocol Returns a string containing the protocol used plus "://", for example "http://".
query_string The query string part of the URL, i.e., everything after "?".
remote_ip The IP address of the client.
url The entire URL used for the request.

6.1.1. query_parameters, request_parameters, and path_parameters

Rails collects all of the parameters for a given request in the params hash, including the ones set in the URL as query string parameters, and those sent as the body of a POST request. The request object has three methods that give you access to the various parameters.

  • query_parameters - contains parameters that were sent as part of the query string.
  • request_parameters - contains parameters sent as part of the post body.
  • path_parameters - contains parameters parsed by the router as being part of the path leading to this particular controller and action.

6.2. The response Object

The response object is built up during the execution of the action from rendering data to be sent back to the client browser. It's not usually used directly but sometimes, in an after_action callback for example, it can be useful to access the response directly. One use case is for setting the content type header:

response.content_type = "application/pdf"

Another use case is for setting custom response headers:

response.headers["X-Custom-Header"] = "some value"

The headers attribute is a hash which maps header names to header values. Rails sets some headers automatically but if you need to update a header or add a custom header, you can use response.headers as in the example above.

The headers method can be accessed directly in the controller as well.

Here are some of the properties of the response object:

Property of response Purpose
body This is the string of data being sent back to the client. This is most often HTML.
status The HTTP status code for the response, like 200 for a successful request or 404 for file not found.
location The URL the client is being redirected to, if any.
content_type The content type of the response.
charset The character set being used for the response. Default is "utf-8".
headers Headers used for the response.

To get a full list of the available methods, refer to the Rails API documentation and Rack Documentation.



Back to top