在這篇文章中,你將學到web快取規則,檔案傳輸中用到的壓縮格式,以及如何手寫程式碼響應請求。最後還能學到快速打包wwwroot資料夾元件用法。
一、瞭解Response Header
當第一次載入程式時,瀏覽器將開啟頁面並下載所有的資源連線。假如頁面沒有錯誤返回都是正確那麼就是返回檔案資料和Http Status為200 -OK的狀態
我們看下這個jquery.min.js檔案Http請求對應的Response Header,這裡會包含ETag值。HTTP內容如下:
ETag: 1d7a4ae31f17d74
ETag :HTTP響應頭是資源的特定版本的識別符號。這可以讓快取更高效,並節省頻寬,因為如果內容沒有改變,Web伺服器不需要傳送完整的響應。而如果內容發生了變化,使用ETag有助於防止資源的同時更新相互覆蓋。如果給定URL中的資源更改,則一定要生成新的Etag值。 因此Etags類似於指紋,也可能被某些伺服器用於跟蹤。 比較etags能快速確定此資源是否變化,但也可能被跟蹤伺服器永久存留。
如果再次請求這個地址的話,瀏覽器將傳送ETag到服務端,如果兩個值沒有變化,那麼服務端會發送304狀態到瀏覽器,那麼瀏覽器將使用之前的資源而不是重新下載一份。
If-None-Match: 1d7a4ae31f17d74
將檔案都快取到了客戶端,這樣就提高了瀏覽器的響應效能,並且通過304狀態,瀏覽器與服務端的請求流量得以減少。
加快瀏覽器的響應效能,還可以設定兩種方案,
1)減少瀏覽器與服務端的請求次數;可以在響應頭內設定Expires,Cache-Control兩個引數。
Expires:響應頭包含日期/時間, 即在此時候之後,響應過期。
Cache-Control:通用訊息頭欄位,被用於在http請求和響應中,通過指定指令來實現快取機制。快取指令是單向的,這意味著在請求中設定的指令,不一定被包含在響應中。
2)壓縮檔案,減少返回流量,從而提高響應效能。
在HTTP 請求時,客戶端會發送Accept-Encoding請求頭給服務端,服務端就可以根據Accept-Encoding請求頭匹配到壓縮方式,將檔案壓縮後返回。
二、DotNet 5 手寫程式碼響應請求
先放主程式碼
1 [HttpGet("_/js/jquery.min.js")]
2 public IActionResult __js_jquery_min_js()
3 {
4 if (SetResponseHeaders("4F252523D4AF0B478C810C2547A63E19") == false) { return StatusCode(304); }
5 const string s = "base64編碼";
6 var bytes = UseCompressBytes(s);
7 return File(bytes, "text/javascript");
8 }
第一行,我們定義請求方式及請求地址。
第二行,我們將請求地址轉成可用的方法名。
第四行,我們設定返回頭,如果返回頭有相同的ETAG值,就返回304。ETAG值可使用Hash值,如Md5。
第五行,獲取檔案的base64編碼,注意檔案先經Brotli 演算法壓縮,然後轉成base64編碼的。這樣有效地減少檔案大小。
第六行,我們嘗試以壓縮的編碼返回。
第七行,我們返回檔案內容及Content-Type型別。
SetResponseHeaders方法如下
1 SetResponseHeaders方法如下
2 private bool SetResponseHeaders(string etag)
3 {
4 if (Request.Headers["If-None-Match"] == etag) { return false; }
5 Response.Headers["Cache-Control"] = "max-age=315360000";
6 Response.Headers["Etag"] = etag;
7 Response.Headers["Date"] = DateTime.Now.ToString("r");
8 Response.Headers["Expires"] = DateTime.Now.AddYears(100).ToString("r");
9 return true;
10 }
我們先對比etag值是否相同,如果相同返回false。不同就設定etag值以及過期時間100年有效。
UseCompressBytes方法如下:
1 private byte[] UseCompressBytes(string s)
2 {
3 var bytes = Convert.FromBase64String(s);
4 var sp = Request.Headers["Accept-Encoding"].ToString().Replace(" ", "").ToLower().Split(',');
5 if (sp.Contains("br")) {
6 Response.Headers["Content-Encoding"] = "br";
7 } else {
8 using (MemoryStream stream = new MemoryStream(bytes)) {
9 using (BrotliStream zStream = new BrotliStream(stream, CompressionMode.Decompress)) {
10 using (var resultStream = new MemoryStream()) {
11 zStream.CopyTo(resultStream);
12 bytes = resultStream.ToArray();
13 }
14 }
15 }
16 if (sp.Contains("gzip")) {
17 Response.Headers["Content-Encoding"] = "gzip";
18 using (MemoryStream stream = new MemoryStream()) {
19 using (GZipStream zStream = new GZipStream(stream, CompressionMode.Compress)) {
20 zStream.Write(bytes, 0, bytes.Length);
21 zStream.Close();
22 }
23 bytes = stream.ToArray();
24 }
25 }
26 }
27 return bytes;
28 }
第一步,判斷是否支援Brotli 演算法壓縮,如果支援就馬上返回。
第二步,使用Brotli 演算法解壓。
第三步,判斷是否支援gzip演算法壓縮,如果支援使用gzip演算法壓縮,然後返回。
第四步,返回原bytes。
三、快速壓縮打包wwwroot資料夾
打包wwwroot資料夾很簡單,但檔案一個一個手寫就會覺得很麻煩。這時就需要ToolGood.WwwRoot元件,在Nuget上直接獲取。
3.1、快速上手
1)新建一個控制檯應用程式,
2)從Nuget上引用ToolGood.WwwRoot元件,
3)新增以下程式碼
1 WwwRootSetting setting = new WwwRootSetting();
2 setting.NameSpace = "ToolGood.TextFilter.Controllers";
3 setting.InFolderPath = @"你的專案路徑\wwwroot";
4 setting.OutFolderPath = @"你的專案路徑\Controllers\wwwroot";
5
6 setting.ExcludeFileSuffixs.Add(".old.js");
7 setting.ExcludeFileSuffixs.Add(".map");
8 setting.BuildControllers();
4)執行控制檯應用程式,就會生成相應的cs檔案。
2.2、WwwRootSetting引數簡介
NameSpace引數設定名稱空間
InFolderPath引數指定wwwroot目錄
OutFolderPath引數指定輸出目錄
ExcludeFileSuffixs引數依據檔案字尾排除檔案
ExcludeFiles引數 依據檔案排除檔案
2.3、其他相關
1)每個生成的檔案都有 #if RELEASE 和 #endif,保證除錯模式下不會被編譯。
2)app.UseStaticFiles();前面新增 #if DEBUG 後面新增 #endif ,保證生成後不會使用本地靜態檔案。
3)程式更新後,靜態檔案過期問題。網上有很多成熟的方案,這裡介紹一個最簡單的方法,使用靜態檔案+程式版本號來解決,如:
<script src="_/js/ok.js?v=20210911"></script>
後記:
.net 5 單檔案執行時會將dll檔案釋放到記憶體內,技術高超的人還是能從記憶體擷取dll檔案。提高dll檔案反編譯成本,我們可以將dll檔案混淆。
dll檔案混淆後會帶來一系列問題,主要是操作麻煩,所以下幾篇將介紹dll檔案混淆、VS調時使用專案原始檔,只在生成時使用dll混淆檔案。