Asp.net-Web-Api

在 ASP.NET Core Web API 中實現 HTTP 記憶體 (ETag)

  • February 17, 2016

我正在開發 ASP.NET Core (ASP.NET 5) Web API 應用程序,並且必須在實體標籤的幫助下實現 HTTP 記憶體。之前我也使用過 CacheCow,但目前它似乎不支持 ASP.NET Core。我也沒有找到任何其他相關的庫或框架支持細節。

我可以為此編寫自定義程式碼,但在此之前我想看看是否有任何可用的東西。如果某些東西已經可用,請分享,以及實現它的更好方法是什麼。

經過一段時間嘗試使其與中間件一起工作後,我發現MVC 動作過濾器實際上更適合此功能。

public class ETagFilter : Attribute, IActionFilter
{
   private readonly int[] _statusCodes;

   public ETagFilter(params int[] statusCodes)
   {
       _statusCodes = statusCodes;
       if (statusCodes.Length == 0) _statusCodes = new[] { 200 };
   }

   public void OnActionExecuting(ActionExecutingContext context)
   {
   }

   public void OnActionExecuted(ActionExecutedContext context)
   {
       if (context.HttpContext.Request.Method == "GET")
       {
           if (_statusCodes.Contains(context.HttpContext.Response.StatusCode))
           {
               //I just serialize the result to JSON, could do something less costly
               var content = JsonConvert.SerializeObject(context.Result);

               var etag = ETagGenerator.GetETag(context.HttpContext.Request.Path.ToString(), Encoding.UTF8.GetBytes(content));

               if (context.HttpContext.Request.Headers.Keys.Contains("If-None-Match") && context.HttpContext.Request.Headers["If-None-Match"].ToString() == etag)
               {
                   context.Result = new StatusCodeResult(304);
               }
               context.HttpContext.Response.Headers.Add("ETag", new[] { etag });
           }
       }
   }        
}

// Helper class that generates the etag from a key (route) and content (response)
public static class ETagGenerator
{
   public static string GetETag(string key, byte[] contentBytes)
   {
       var keyBytes = Encoding.UTF8.GetBytes(key);
       var combinedBytes = Combine(keyBytes, contentBytes);

       return GenerateETag(combinedBytes);
   }

   private static string GenerateETag(byte[] data)
   {
       using (var md5 = MD5.Create())
       {
           var hash = md5.ComputeHash(data);
           string hex = BitConverter.ToString(hash);
           return hex.Replace("-", "");
       }            
   }

   private static byte[] Combine(byte[] a, byte[] b)
   {
       byte[] c = new byte[a.Length + b.Length];
       Buffer.BlockCopy(a, 0, c, 0, a.Length);
       Buffer.BlockCopy(b, 0, c, a.Length, b.Length);
       return c;
   }
}

然後在您想要作為屬性的操作或控制器上使用它:

[HttpGet("data")]
[ETagFilter(200)]
public async Task<IActionResult> GetDataFromApi()
{
}

中間件和過濾器之間的重要區別在於,您的中間件可以在 MVC 中間件之​​前和之後執行,並且只能與 HttpContext 一起使用。同樣,一旦 MVC 開始將響應發送回客戶端,就為時已晚,無法對其進行任何更改。

另一方面,過濾器是 MVC 中間件的一部分。他們可以訪問 MVC 上下文,在這種情況下,實現此功能會更簡單。更多關於過濾器及其在 MVC 中的管道。

引用自:https://stackoverflow.com/questions/35458737