http響應Last-Modified和ETag以及asp.net web api實現

來源:互聯網
上載者:User

基礎知識
1) 什麼是”Last-Modified”?
在瀏覽器第一次請求某一個URL時,伺服器端的返回狀態會是200,內容是你請求的資源,同時有一個Last-Modified的屬性標記此檔案在服務期端最後被修改的時間,格式類似這樣:
Tue, 24 Apr 2012 13:53:56 GMT

用戶端第二次請求此URL時,根據 HTTP 協議的規定,瀏覽器會向伺服器傳送 If-Modified-Since 前序,詢問該時間之後檔案是否有被修改過:
If-Modified-Since: Tue, 24 Apr 2012 13:53:56 GMT

如果伺服器端的資源沒有變化,則自動返回 HTTP 304 (Not Changed.)狀態代碼,內容為空白,這樣就節省了傳輸資料量。當伺服器端代碼發生改變或者重啟伺服器時,則重新發出資源,返回和第一次請求時類似。從而保證不向用戶端重複發出資源,也保證當伺服器有變化時,用戶端能夠得到最新的資源。
2) 什麼是”Etag”?
HTTP 協議規格說明定義ETag為“被請求變數的實體值” 。 另一種說法是,ETag是一個可以與Web資源關聯的記號(token)。典型的Web資源可以一個Web頁,但也可能是JSON或XML文檔。伺服器單獨負責判斷記號是什麼及其含義,並在HTTP回應標頭中將其傳送到用戶端,以下是伺服器端返回的格式:
"9077da2dec72bbb7151a6579fa214de0"

用戶端的查詢更新格式是這樣的:
"9077da2dec72bbb7151a6579fa214de0"

如果ETag沒改變,則返回狀態304然後不返回,這也和Last-Modified一樣。

Last-Modified和Etags如何協助提高效能?
聰明的開發人員會把Last-Modified 和ETags請求的http前序一起使用,這樣可利用用戶端(例如瀏覽器)的緩衝。因為伺服器首先產生 Last-Modified/Etag標記,伺服器可在稍後使用它來判斷頁面是否已經被修改。本質上,用戶端通過將該記號傳回伺服器要求伺服器驗證其(用戶端)緩衝。

過程如下:
1,用戶端請求一個頁面(A)。

2,伺服器返回頁面A,並在給A加上一個Last-Modified/ETag。

3,用戶端展現該頁面,並將頁面連同Last-Modified/ETag一起緩衝。

4,客戶再次請求頁面A,並將上次請求時伺服器返回的Last-Modified/ETag一起傳遞給伺服器。

5,伺服器檢查該Last-Modified或ETag,並判斷出該頁面自上次用戶端請求之後還未被修改,直接返迴響應304和一個空的響應體。

正確使用Etag和Expires標識處理,可以使得頁面更加有效被Cache。
在用戶端通過瀏覽器發出第一次請求某一個URL時,根據 HTTP 協議的規定,瀏覽器會向伺服器傳送前序(Http Request Header),伺服器端響應同時記錄相關屬性標記(Http Reponse Header),伺服器端的返回狀態會是200,格式類似如下:

HTTP/1.1 200 OK
Cache-Control: public, max-age=1728000
Transfer-Encoding: chunked
Content-Type: image/jpeg
Expires: Sun, 20 May 2012 16:26:22 GMT
Last-Modified: Tue, 24 Apr 2012 13:53:56 GMT
ETag: "9077da2dec72bbb7151a6579fa214de0"
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Apr 2012 16:26:22 GMT

用戶端第二次請求此URL時,根據 HTTP 協議的規定,瀏覽器會向伺服器傳送前序(Http Request Header),伺服器端響應並記錄相關記錄屬性標記檔案沒有發生改動,伺服器端返回304,直接從緩衝中讀取:

HTTP/1.1 304 Not Modified
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Apr 2012 16:30:06 GMT

 

asp.net web api的實現代碼如下:

// GET /img/2012031023134652.png
        [HttpGet]
        public HttpResponseMessage Get([FromUri]string filename)
        {
            HttpResponseMessage response = new HttpResponseMessage();  

            MongoFSDirectory fs = new MongoFSDirectory(new MongoFSDirectoryParameters() { ConnectionString = this.ConnectionString });
            MongoGridFSFileInfo fileInfo = fs.GetFileInfo(filename);
            if (fileInfo == null)
            {
                throw new HttpResponseException("The file does not exist.", HttpStatusCode.NotFound);
            }
            string etag = string.Format("\"{0}\"", fileInfo.MD5);
            var tag = Request.Headers.IfNoneMatch.FirstOrDefault();
            if (Request.Headers.IfModifiedSince.HasValue && tag != null && tag.Tag == etag)
            {
                response.StatusCode = HttpStatusCode.NotModified;
            }
            else
            {
                MemoryStream responseStream = new MemoryStream();
                MongoGridFSStream gfs = fileInfo.OpenRead();

                bool fullContent = true;
                if (this.Request.Headers.Range != null)
                {
                    fullContent = false;
                    // Currently we only support a single range.
                    RangeItemHeaderValue range = this.Request.Headers.Range.Ranges.First();

                    // From specified, so seek to the requested position.
                    if (range.From != null)
                    {
                        gfs.Seek(range.From.Value, SeekOrigin.Begin);

                        // In this case, actually the complete file will be returned.
                        if (range.From == 0 && (range.To == null || range.To >= gfs.Length))
                        {
                            gfs.CopyTo(responseStream);
                            fullContent = true;
                        }
                    }
                    if (range.To != null)
                    {
                        // 10-20, return the range.
                        if (range.From != null)
                        {
                            long? rangeLength = range.To - range.From;
                            int length = (int)Math.Min(rangeLength.Value, gfs.Length - range.From.Value);
                            byte[] buffer = new byte[length];
                            gfs.Read(buffer, 0, length);
                            responseStream.Write(buffer, 0, length);
                        }
                        // -20, return the bytes from beginning to the specified value.
                        else
                        {
                            int length = (int)Math.Min(range.To.Value, gfs.Length);
                            byte[] buffer = new byte[length];
                            gfs.Read(buffer, 0, length);
                            responseStream.Write(buffer, 0, length);
                        }
                    }
                    // No Range.To
                    else
                    {
                        // 10-, return from the specified value to the end of file.
                        if (range.From != null)
                        {
                            if (range.From < gfs.Length)
                            {
                                int length = (int)(gfs.Length - range.From.Value);
                                byte[] buffer = new byte[length];
                                gfs.Read(buffer, 0, length);
                                responseStream.Write(buffer, 0, length);
                            }
                        }
                    }
                }
                // No Range header. Return the complete file.
                else
                {
                    gfs.CopyTo(responseStream);
                }
                gfs.Close();
                responseStream.Position = 0;
                response.StatusCode = fullContent ? HttpStatusCode.OK : HttpStatusCode.PartialContent;
                response.Content = new StreamContent(responseStream);
                response.Content.Headers.ContentType = new MediaTypeHeaderValue(fileInfo.ContentType);
                response.Headers.ETag = new EntityTagHeaderValue(etag);
                response.Headers.CacheControl = new CacheControlHeaderValue();
                response.Headers.CacheControl.Public = true;
                response.Headers.CacheControl.MaxAge = TimeSpan.FromHours(480);
                response.Content.Headers.Expires = DateTimeOffset.Now.AddDays(20);
                response.Content.Headers.LastModified = fileInfo.UploadDate;
            }
            return response;

        }

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.