使用 flash


HTTP是無狀態的通訊協定,對伺服器來說,每次都是新請求,為了要讓伺服器得知上次請求與此次請求的關係,有一些會話管理機制,像是先前介紹過的 隱藏欄位Cookie 或高階的 session 機制。

Rails提供的flash方法,也是一種高階的會話機制,其作用週期僅止於兩次請求之間。如果在此次請求時如下設置:

flash[:notice] = "Post successfullly created"

再次瀏覽請求時,可以如下取得訊息(假設是在樣版檔案中):

<%= flash[:notice] %>

這次請求結束後,flash[:notice]設置的訊息就清除了,第三次請求時若嘗試flash[:notice]就不會再取得任何訊息。經常應用的場合是在redirect_to前後,在要求重新導向前使用flash設置訊息,瀏覽器被重新導向後利用flash取得訊息

實際上,flash方法會嘗試使用session方法指定"flash"取得ActionDispatch::Flash::FlashHash實例,如果沒有就建立新的ActionDispatch::Flash::FlashHash實例:

# File actionpack/lib/action_dispatch/middleware/flash.rb, line 6
def flash
  @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new)
end

所以flash底層是利用session機制來處理,上次請求設定訊息用的ActionDispatch::Flash::FlashHash實例,其實會使用session設定名稱為"flash"加以儲存,下次請求結束後,session中設定的ActionDispatch::Flash::FlashHash實例就會被清除,因此取名為flash,代表的就是訊息在兩個請求間一閃而過

由於使用flash設定與取得:notice、:alert是常見需求,因此ActionDispatch::Flash::FlashHash也提供notice與alert方法。例如:

flash.notice = "Post successfully created" # 可於下次請求使用flash.notice取得訊息
flash.alert = "Post failed" # 可於下次請求使用flash.alert取得訊息

實際上原始碼是:

# File actionpack/lib/action_dispatch/middleware/flash.rb, line 200
def alert
  self[:alert]
end

# File actionpack/lib/action_dispatch/middleware/flash.rb, line 205
def alert=(message)
  self[:alert] = message
end

# File actionpack/lib/action_dispatch/middleware/flash.rb, line 210
def notice
  self[:notice]
end

# File actionpack/lib/action_dispatch/middleware/flash.rb, line 215
def notice=(message)
  self[:notice] = message
end

有時候,你只想讓flash設定的訊息,只在此次請求有用。例如:

def do_some
    ....
    if some_cond?
        flash.alert = "something must be wrong"
        redirect_to "/test/error"
    end
end

def do_other
    ...
    if some_cond?
        flash.alert = "shit happens"
        render "/test/error"
    end
end

也這"test/error"的樣版是會共用的錯誤處理樣版,都是這麼寫:

<%= flash.alert %>

在do_some時會要求重新導向,但是在do_other時你想直接呈現,然而以上的寫法,do_other結束後,重新請求還是可以取得上次flash.alert的訊息。你應該使用flash.now。例如

def do_other
    ...
    if some_cond?
        flash.now.alert = "shit happens"
        render "/test/error"
    end
end

flash.now會使用ActionDispatch::Flash::FlashNow將原有的ActionDispatch::Flash::FlashHash實例包裹後傳回

# File actionpack/lib/action_dispatch/middleware/flash.rb, line 158
def now
  @now ||= FlashNow.new(self)
end

ActionDispatch::Flash::FlashNow實例設定的訊息,會在此次請求之後清除,因此下次請求就取不到訊息了

如果在下次請求使用過flash設定的訊息後,想要保有某些訊息至第三次請求,其它訊息棄用,則可以使用keep方法。例如第一次請求若為:

flash[:notice1] = "test1"
flash[:notice2] = "test2"

第二次請求時,若呼叫了:

flash.keep(:notice1)

則第二次請求結束後,:notice1被保留,:notice2被丟棄,因此第三次請求時,可以取得flash[:notice1]的值,但取不到flash[:notice2]的值如果呼叫keep時沒有指定保留哪些訊息,則所有訊息保留至下一次請求。discard則是與keep作用相反的方法,被指定的訊息將被丟棄,其它訊息再保留至下一次請求