Cookie


Web應用程式會話管理的基本方式,就是在此次請求中,將下一次請求時伺服器所應知道的資訊,先回應給瀏覽器,由瀏覽器在之後的請求再一併發送給應用程式,如此應用程式就可以「得知」多次請求間的相關資料。

Cookie是在瀏覽器儲存訊息的一種方式,伺服器可以回應瀏覽器set-cookie標頭,瀏覽器收到這個標頭與數值後,會將之儲存為電腦上的一個檔案,這個檔案就稱之為Cookie。你可以設定給Cookie一個存活期限,保留一些有用的訊息在客戶端,大小限制是4KB,如果關閉瀏覽器之後,再度開啟瀏覽器並連接伺服器,而Cookie仍在有效期限中,瀏覽器會使用cookie標頭自動將Cookie發送給伺服器,伺服器就可以得知一些先前瀏覽器請求的相關訊息。

Cookie

Cookie可以設定存活期限,所以在客戶端儲存的資訊可以活得更久一些(除非使用者主動清除Cookie資訊)。有些購物網站常使用Cookie來記錄 使用者的瀏覽歷程,雖然使用者沒有實際購買商品,但在下次使用者造訪時,仍可以根據Cookie中所儲存的瀏覽歷程為使用者建議購物清單。

如果你要建立Cookie,可以呼叫cookies方法,它會傳回ActionDispatch::Cookies::CookieJar實例,它定義了[]與[]=方法,可以指定或取得Cookie中的名稱與數值。例如:

cookies[:user] = "caterpillar"

使用cookies[:user]或cookies["user"]都是相同的,這使得回應中帶有以下標頭:

Set-Cookie    user=caterpillar; path=/

如果要設定Cookie存活期限或其它選項,可以如下:

cookies[:user] = {
    :value => "caterpillar",
    :expires => 2.week.from_now,
    :domain => "openhome.cc"
}

如範例中所示,可以使用:expires設定Cookie的有效期限,要設定的物件是ActiveSupport::TimeWithZone實例。預設是關閉瀏覽器之後Cookie就失效。如果要設定儘可能永不失效的Cookie,可以使用permanent方法(其實是從建立起的20年後失效XD),它會傳回ActionDispatch::Cookies::PermanentCookieJar(繼承自ActionDispatch::Cookies::CookieJar)實例,例如:

cookies.permanent[:login] = "XJ-122"

如果要取得瀏覽器送出的某個Cookie,可以直接cookies[:key]取得,其中:key是Cookie的鍵,如果要取得所有Cookie,則可以使用each方法。例如:

cookies.each do |key, value|
    # .... 作一些事
end

如果要刪除Cookie,可以使用delete方法,會將指定的Cookie設定為空字串。例如:

cookies.delete :user
cookies.delete(:user, :domain => "openhome.cc")

運用Cookie的另一個常見應用就是實作使用者自動登入(Login)功能。在使用者登入表單上,你應該經常看到有個自動登入的選項,登入時若有核取該選項,下次再造訪同一網頁,就不再需要輸入名稱密碼,而可以直接登入網頁。

以下將實作一個簡單的範例來示範Cookie API的使用。當使用者造訪首頁時,會檢查使用者先前是否允許自動登入,如果是的話,就直接將轉送至使用者頁面:

  • tests_controller.rb
class TestsController < ApplicationController
    def index
       if cookies[:user] == "caterpillar"
          @user = cookies[:user]
          render "user"
       else
          redirect_to :action => "login"
       end
    end

    def login
        if params[:user] == "caterpillar" && params[:passwd] == "123456"
            if params[:auto_login]
                cookies[:user] = { :value => params[:user], :expires => 2.week.from_now }
            end
            @user = params[:user]
            render "user"
        end
    end
end

當使用者造訪首頁時,會檢查是否有Cookie儲存鍵"user"而值為"caterpillar",如果是的話,表示先前使用者登入時,曾經核取「自動登入」的選項,因此直接呈現使用者網頁。如果沒有對應的Cookie,則表示使用者是初次造訪,或者先前沒有核取「自動登入」的選項,此時重導至登入表單:
  • login.html.erb
<%= form_tag "login" do %>
    <%= label_tag "user", "User: " %><%= text_field_tag "user" %><br>
    <%= label_tag "passwd", "Password: " %><%= password_field_tag "passwd" %><br>
    <%= label_tag "auto_login", "Auto login: " %><%= check_box_tag "auto_login" %><br>
    <%= submit_tag "Login" %>
<% end %>

當登入名稱與密碼正確時,呈現使用者頁面(你可以自已製作一個user.html.erb),否則直接再呈現登入頁面。

為了避免客戶端的Cookie被修改,你可以使用signed方法,這會傳回ActionDispatch::Cookies::SignedCookieJar(繼承自ActionDispatch::Cookies::CookieJar)實例,例如:
cookies.signed[:user] = "caterpillar"
cookies.permanent.signed[:user] = "caterpillar"