Processing of Requests

Learn to further process requests after getting a REST request.

In the previous lesson, we worked out how Action Dispatch routes an incoming request to the appropriate code in your application. Now let’s see what happens inside that code.

Action methods

When a controller object processes a request, it looks for a public instance method with the same name as the incoming action. If it finds one, that method is invoked. If it doesn’t find one and the controller implements method_missing(), that method is called, passing in the action name as the first parameter and an empty argument list as the second. If no method can be called, the controller looks for a template named after the current controller and action. If found, this template is rendered directly. If none of these things happens, an AbstractController::ActionNotFound error is generated.

Controller environment

The controller sets up the environment for actions (and, by extension, for the views that they invoke). Many of these methods provide direct access to the information contained in the URL or request:

  • action_name

    The name of the action currently being processed.

  • cookies

    The cookies are associated with the request. Setting values into this object stores cookies on the browser when the response is sent. Rails support for sessions is based on cookies.

  • headers

    A hash of HTTP headers will be used in the response. By default, Cache-Control is set to no-cache. We might want to set Content-Type headers for special-purpose applications. Note that we shouldn’t set cookie values in the header directly. Use the cookie API to do this.

  • params

    A hash-like object containing request parameters along with pseudo parameters generated during routing. It’s hash-like because we can index entries using either a symbol or a string. The params[:id] and params['id'] return the same value. Idiomatic Rails applications use the symbol form.

  • request

    The incoming request object. It includes these attributes:

    • The request_method attribute returns the request method, one of :delete, :get, :head, :post, or :put.

    • The method attribute returns the same value as request_method except for :head, which it returns as :get because these two are functionally equivalent from an application’s point of view.

    • The delete?, get?, head?, post?, and put? attribute return true or false based on the request method.

    • The xml_http_request? and xhr? attribute return true if this request was issued by one of the Ajax helpers. Note that this parameter is independent of the method parameter.

    • The url() attribute, which returns the full URL used for the request.

    • The protocol(),host(),port(),path(), and query_string()attribute, which return components of the URL used for the request based on the following pattern: protocol://host:port/path?query_string.

    • The domain()attribute, which returns the last two components of the domain name of the request.

    • The host_with_port()attribute, which is a host:port string for the request.

    • The port_string()attribute, which is a :port string for the request if the port is not the default port (80 for HTTP, 443 for HTTPS).

    • The ssl?()attribute, which is true if this is an SSL request. In other words, the request was made with the HTTPS protocol.

    • Tbe remote_ip() attribute, which returns the remote IP address as a string. The string may have more than one address in it if the client is behind a proxy.

    • The env() attribute, which is the environment of the request. We can use this to access values set by the browser, like:

      request.env['HTTP_ACCEPT_LANGUAGE']

    • The accepts() attribute, which is an array with Mime::Type objects that represent the MIME types in the Accept header.

    • The format() attribute, which is computed based on the value of the Accept header, with Mime[:HTML] as a fallback.

    • The content_type() attribute, which is the MIME type for the request. This is useful for put and post requests.

    • The headers() attribute, which is the complete set of HTTP headers.

    • The body() attribute, which is the request body as an I/O stream.

    • The content_length() attribute, which is the number of bytes purported to be in the body.

  • response

    The response object, filled in during the handling of the request. Normally, this object is managed for us by Rails.

  • session

    A hash-like object representing the current session data.

In addition, a logger is available throughout Action Pack.

Responding to the user

Part of the controller’s job is to respond to the user. There are basically four ways of doing this:

  • The most common way is to render a template. In terms of the MVC paradigm, the template is the view. It takes the information provided by the controller and uses it to generate a response to the browser.
  • The controller can return a string directly to the browser without invoking a view. This is fairly rare but can be used to send error notifications.
  • The controller can return nothing to the browser. This is sometimes used when responding to an Ajax request. In all cases, however, the controller returns a set of HTTP headers, because some kind of response is expected.
  • The controller can send something other than HTML data to the client. This is typically a download of some kind, such as a PDF document or a file’s contents.

A controller always responds to the user exactly one time per request. This means we should have just one call to a render(), redirect_to(), or send_xyz() method in the processing of any request. A DoubleRenderError exception is thrown on the second render.

Because the controller must respond exactly once, it checks to see whether a response has been generated just before it finishes handling a request. If not, the controller looks for a template named after the controller and action and automatically renders it. This is the most common way that rendering takes place. You may have noticed that in most of the actions in our shopping cart tutorial we never explicitly rendered anything. Instead, our action methods set up the context for the view and return. The controller notices that no rendering has taken place and automatically invokes the appropriate template.

We can have multiple templates with the same name but with different extensions. For example, .html.erb, .xml.builder, and .js.erb. If we don’t specify an extension in a render request, Rails assumes html.erb.

Rendering templates

A template is a file that defines the content of a response for our application. Rails supports three template formats out of the box. It uses erb, which is embedded Ruby code typically with HTML, builder, a more programmatic way of constructing XML content, and RJS, which generates JavaScript.

By convention, the template for action of controller will be in the file app/views/controller/action.type.xxx. In this case, type is the file type, including html, atom, or js, and xxx is one of erb, builder or scss. The app/views part of the name is the default. We can override this for an entire application by setting this:

ActionController.prepend_view_path dir_path

The render() method is the heart of all rendering in Rails. It takes a hash of options that tell it what to render and how to render it.

It’s tempting to write code in our controllers that look like this:

Get hands-on with 1200+ tech skills courses.