Chapter 13. Sessions and Cookies

The Web was built to do one thing at a time. Each request is, from the point of view of the client and server, completely independent of every other. A group of requests might all operate on the same database, and there can be clear paths from one part of an application to another, but for the most part, HTTP and scalable web application design both try to keep requests as independent as possible. It makes the underlying infrastructure easier.

Rails balances that simplicity of infrastructure with application developers’ need for a coherent way to maintain context. Rails supports several mechanisms keeping track of information about users. If you want to keep track of users manually, you can work with cookies. If you want to keep track of users for a brief series of interactions, Rails’ built-in session support should meet your needs.

Note

If you want to keep track of users on a long-term basis, you’ll want to use the authentication tools covered in Chapter 14.

Getting Into and Out of Cookies

Like nearly every web framework, Rails provides support for cookies. Cookies are small pieces of text, usually less than 4 KB long, that servers can set on browsers and browsers will pass back with requests to servers. Browsers keep track of where cookies came from and only report cookies’ values to the server where they came from originally. JavaScript code can reach into a cookie from a web page, but Rails itself is more interested in setting and receiving cookies through the HTTP headers for each request and response.

Warning

When cookies first appeared, they were loved by developers who saw them as a way to keep track of which user was visiting their site, and hated by privacy advocates. Much of that uproar has calmed, because cookies have become a key part of functionality that users like, but there’s still potential for abuse.

To stay on the good side of potentially cranky users, it’s best to set cookie lifetimes to relatively brief periods and use longer cookies only when users request them (as in the classic “remember me” checkboxes for logins). Never store sensitive information directly in cookies, either!

In most cases, your application probably doesn’t need to access cookies directly. Rails’ built-in support for sessions and the tools for user authentication can both manage all of the overhead of keeping track of users for you. However, if you want to use cookies directly, either because you have specific needs for them or because you’re interacting with other code (say, a JavaScript library) that expects a particular cookie to provide it with a key value, then the demonstration below should give you a clear idea how it works. Figure 13-1 provides an overall picture of how cookies flow through an application.

The flow of cookies between Rails, the browser, and code in the browser

Figure 13-1. The flow of cookies between Rails, the browser, and code in the browser

Because cookies are about storing data on the client, not the server, a really simple example will do. To get started, this example will build on one of the simplest examples in this book so far, the first version of the entry controller with its sign_in method from Chapter 4. (Code for this example is in ch13/guestbook011.)

Note

If you’d rather create a new blank copy of this application, run rails guestbook, then cd guestbook if necessary, and then finally ruby script/generate controller entry.

Example 13-1 shows the new app/controllers/entry_controller.rb file, with changes from the Chapter 4 version in bold.

Example 13-1. Keeping track of names entered with a cookie

class EntryController < ApplicationController
   def sign_in
    @previous_name = cookies[:name]
    @name = params[:visitorName]
    cookies[:name] = @name
  end
end

The new first line collects the previous name entered from the cookie and stores it as @previous_name so the view can display it. (The cookie data comes to the server through the HTTP request headers.) The second line, as before, gathers the new name from the :visitorName field of the form, and the third name stores that name (even if it’s empty) as a cookie that will be transmitted to the browser through the HTTP response headers.

The view in app/views/entry/sign_in.html.erb just needs three extra lines to show the previous name if there was one, as shown in Example 13-2.

Example 13-2. Reporting a previous name to the user

<html>
<head><title>Hello <%=h @name %></title></head>

<body>
<h1>Hello <%=h @name %></h1>
<% form_tag :action => 'sign_in' do %>
   <p>Enter your name:
   <%= text_field_tag 'visitorName', @name %></p>

   <%= submit_tag 'Sign in' %>
<% end %>
<% if @previous_name != '' %>
<p>Hmmm... the last time you were here, you said you were <%=h @previous_name
%>.</p>
<% end %>
</body>
</html>

This tests to see whether a previous name was set and, if so, presents the user with what they’d entered. All this really does is demonstrate that the cookie is keeping track of something entered in a past request, making it available to the current request.

The HTTP headers that carry the cookie back and forth are normally invisible, though not that interesting. You can see cookie information in most browsers through a preferences or info setting. At the beginning, this application looks much like its predecessor, as shown in Figure 13-2.

A simple name form, though now one with a cookie behind it

Figure 13-2. A simple name form, though now one with a cookie behind it

In Firefox 3.0, you call up the cookie inspection window at Tools/Page Info, then the Security tab, and then the View Cookies button halfway down the screen on the righthand side. You’ll see something like Figure 13-3.

For now, the :name cookie is the one that matters, and as you can see, its content is blank. It came from localhost, because this is a test session on the local machine. The path is set to /, the Rails default, making it accessible to any page that comes from the localhost server. It gets sent with all HTTP connections and will expire “at end of session”—as soon as the user quits the browser. Users can, of course, delete the cookie immediately with the Remove Cookie button.

If you enter a name, say, “Zimton,” and click the “Sign-in” button, you’ll see something like Figure 13-4.

A cookie named “name” with a blank value

Figure 13-3. A cookie named “name” with a blank value

The form, with a new name set

Figure 13-4. The form, with a new name set

Because the :name cookie was previously set to an empty string, the query message still isn’t shown, but this time the trigger is set. If you inspect the cookie, you’ll see that the :name cookie’s value is now “Zimton,” as shown in Figure 13-5.

The name cookie, now set with a value of “Zimton”

Figure 13-5. The name cookie, now set with a value of “Zimton”

If you enter a new name, say “Zimtonito,” and click the “Sign-in” button, the Rails application will get “Zimtonito” through the form, while still getting “Zimton” from the cookie. This time, it will ask why the name has changed, as shown in Figure 13-6.

Changing names over the session produces a response

Figure 13-6. Changing names over the session produces a response

Storing the name information in the cookie gives Rails a memory of what happened before and lets it notice a change.

If you choose to use cookies directly, rather than relying on Rails’ other mechanisms for keeping track of interactions across requests, there are a few more parameters you should know about when setting cookies. If you set more than just a value for a cookie, the syntax changes. To set both a value and a path for the :name cookie, for example, you would change:

cookies[:name] = @name

to:

cookies[:name] = { :value => @name, :path => '/entry' }

The available parameters include:

:value

The value for the cookie, usually a short string. (Typically this is a database key, but make sure not to store anything genuinely secret.)

:domain

The domain to which the cookie applies. This has to be a domain that matches with the domain the application runs at. For example, if an application was hosted at http://myapp.example.com, :domain could be set to http://myapp.example.com or http://example.com. If it was set to http://example.com, the cookie could be read from http://myapp.example.com, http://yourapp.example.com, or http://anything.example.com.

:path

The path to which the cookie applies. Like :domain, the :path must be all or part of the path from which the call is being made. From /entry/sign_in, it could be set to /, to /entry, or to /entry/sign_in. The cookie can only be read from URLs that could have set that path. (By default, this is /, making the cookie available to everything at your domain.)

:expires

The time at which the cookie will expire. The easiest way to set this is with Ruby’s time methods, such as 5.minutes or 12.hours.from_now.

:secure

If set to true, the cookie is only reported or sent over secure HTTP (HTTPS) connections.

:http_only

If true, the cookie is transmitted over HTTP or HTTPS connections, but is not available to JavaScript code on those pages.

Anytime you find yourself using cookies, especially if you’re doing complicated things with cookies, you should consider using sessions or authentication instead.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset