﻿# 如何辨識使用者從瀏覽器按下 F5 或 Ctrl + R 重新整理頁面

有時候有些頁面有計數器或者有些程式碼只需要讓使用者執行一遍而已，我們會不希望使用者拼命用 F5 或 Ctrl + R 重新整理頁面時擾亂程式的運作，要達成這個需求其實有很多作法，例如透過一個 Cookie、一個 Session 來做判斷，但今天我要分享一個暗黑密記，教你如何從 HTTP Request Header 就可以判斷出使用者正在執行該頁面的重新整理。

每個瀏覽器對於使用者重新整理頁面時，都有一個固定的行為模式，就是會特別送出一個 HTTP Header 告訴伺服器或 Proxy Server 不要快取該頁面，經過我的實際測試，IE 就是與其他瀏覽器不太一樣，但我想 IE 有他合理的用意存在，容後再述。

在 Internet Explorer 瀏覽器 ( 包括 IE8 ) 當使用者重新整理頁面時，瀏覽器會特別發出一個 Pragma 標頭:

```
Pragma: no-cache
```

在 Firefox 或 Google Chrome 瀏覽器則會送出 HTTP/1.1 的 Cache-Control 標頭:

```
Cache-Control: max-age=0
```

雖然在 HTTP/1.1 也有定義 Pragma 標頭，但是也有提到這個標頭是為了向 HTTP/1.0 相容才存在的，但 HTTP/1.1 規範的 [14.32 Pragma](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32) 中也有特別提到，如果瀏覽器(Client)不確定伺服器(Server)是否可支援 HTTP/1.1 時，**應該**兩個 Header 一起送出，基於這一點，各家瀏覽器好像沒人在裡會 W3C 的建議，FF 與 Chrome 完全不理會 HTTP/1.0 的存在，而 IE 則是比較照顧舊的伺服器。

基於這個特性，我們可以很容易的將以下程式碼放置在 BasePage 或 BaseController 中，以供頁面使用:

```
bool IsRefresh{    get    {        return this.Request.Headers["Pragma"] == "no-cache" ||               this.Request.Headers["Cache-Control"] == "max-age=0";    }}
```

不過這個技巧只能用在 HTTP GET 的環境，如果是 HTTP POST 表單送出的話，瀏覽器也一樣會送出這個 HTTP 標頭，所以當 HTTP POST 時不能用來判斷是否使用者按下重新整理按鈕或按 F5 或 Ctrl + R 等快速鍵。

額外一提，此技巧跟 ASP.NET Web Form 的 [IsPostBack](http://msdn.microsoft.com/en-us/library/system.web.ui.page.ispostback.aspx) 是完全不一樣的意思，可千萬不要搞混了。

另外 Web 開發人員也有一個常見的困擾，就是當表單透過 HTTP POST 送出後顯示結果時客戶在這個時候再次按下 F5 或 Ctrl + R 重新整理頁面，這時表單的資料就會重新送出一次，如果不好好處理就會讓完全相同的資料儲存到資料庫中。

我對這種情況的處理通常是不要在 POST 成功後留在同一頁，最好是能將顯示表單結果的頁面透過 HTTP Redirect 轉向到另一頁去。由於 HTTP POST 時瀏覽器會送出「不要快取」的指令，且透過 HTTP 3xx 轉向的頁面不管是瀏覽器或 Proxy Server 也都不會快取，因此「直接轉向到另一頁」的好處是使用者沒有機會簡單的按下「重新整理」就重新送出一次表單。

不過若客戶先回上一頁，然後再按一次「送出」按鈕，還是會有可能會發生重複送出表單的情況，這時就要用另外的技巧處理了，我比較常用的作法是透過 JavaScript 的 onload 事件搭配 Cookie 判斷將已送出的表單內容清空，這通常是在要求非常龜毛的情況下才會用這種技巧。

**相關連結**

-   [HTTP - Hypertext Transfer Protocol](http://www.w3.org/Protocols/)
