gpt4 book ai didi

c# - 如何使用 Action 过滤器和 HttpResponseMessage 在 Web API 中使用 ETag

转载 作者:可可西里 更新时间:2023-11-01 03:01:05 26 4
gpt4 key购买 nike

我有一个 ASP.Net Web API Controller ,它只返回用户列表。

public sealed class UserController : ApiController
{
[EnableTag]
public HttpResponseMessage Get()
{
var userList= this.RetrieveUserList(); // This will return list of users
this.responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ObjectContent<List<UserViewModel>>(userList, new JsonMediaTypeFormatter())
};
return this.responseMessage;
}
}

和一个负责管理ETag和缓存的 Action 过滤器属性类EnableTag:

public class EnableTag : System.Web.Http.Filters.ActionFilterAttribute
{
private static ConcurrentDictionary<string, EntityTagHeaderValue> etags = new ConcurrentDictionary<string, EntityTagHeaderValue>();

public override void OnActionExecuting(HttpActionContext context)
{
if (context != null)
{
var request = context.Request;
if (request.Method == HttpMethod.Get)
{
var key = GetKey(request);
ICollection<EntityTagHeaderValue> etagsFromClient = request.Headers.IfNoneMatch;

if (etagsFromClient.Count > 0)
{
EntityTagHeaderValue etag = null;
if (etags.TryGetValue(key, out etag) && etagsFromClient.Any(t => t.Tag == etag.Tag))
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotModified);
SetCacheControl(context.Response);
}
}
}
}
}

public override void OnActionExecuted(HttpActionExecutedContext context)
{
var request = context.Request;
var key = GetKey(request);

EntityTagHeaderValue etag;
if (!etags.TryGetValue(key, out etag) || request.Method == HttpMethod.Put || request.Method == HttpMethod.Post)
{
etag = new EntityTagHeaderValue("\"" + Guid.NewGuid().ToString() + "\"");
etags.AddOrUpdate(key, etag, (k, val) => etag);
}

context.Response.Headers.ETag = etag;
SetCacheControl(context.Response);
}

private static void SetCacheControl(HttpResponseMessage response)
{
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromSeconds(60),
MustRevalidate = true,
Private = true
};
}

private static string GetKey(HttpRequestMessage request)
{
return request.RequestUri.ToString();
}
}

以上代码创建了一个属性类来管理ETag。因此,在第一个请求中,它将创建一个新的 E-Tag,对于后续请求,它将检查是否存在任何 ETag。如果是这样,它将生成 Not Modified HTTP 状态并返回给客户端。

我的问题是,如果我的用户列表发生变化,我想创建一个新的 ETag,例如。添加新用户,或删除现有用户。并将其附加到响应中。这可以通过 userList 变量进行跟踪。

目前,从客户端和服务器接收到的 ETag 在每个第二个请求中都是相同的,因此在这种情况下,它将始终生成 Not Modified 状态,而我想要的是实际上没有任何变化。

谁能指导我朝这个方向发展?

最佳答案

我的要求是缓存我的 web api JSON 响应......并且提供的所有解决方案都没有一个简单的“链接”到生成数据的位置 - 即在 Controller 中......

所以我的解决方案是创建一个包装器“CacheableJsonResult”,它生成一个响应,然后将 ETag 添加到 header 。这允许在生成 Controller 方法并想要返回内容时传入一个etag...

public class CacheableJsonResult<T> : JsonResult<T>
{
private readonly string _eTag;
private const int MaxAge = 10; //10 seconds between requests so it doesn't even check the eTag!

public CacheableJsonResult(T content, JsonSerializerSettings serializerSettings, Encoding encoding, HttpRequestMessage request, string eTag)
:base(content, serializerSettings, encoding, request)
{
_eTag = eTag;
}

public override Task<HttpResponseMessage> ExecuteAsync(System.Threading.CancellationToken cancellationToken)
{
Task<HttpResponseMessage> response = base.ExecuteAsync(cancellationToken);

return response.ContinueWith<HttpResponseMessage>((prior) =>
{
HttpResponseMessage message = prior.Result;

message.Headers.ETag = new EntityTagHeaderValue(String.Format("\"{0}\"", _eTag));
message.Headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromSeconds(MaxAge)
};

return message;
}, cancellationToken);
}
}

然后,在您的 Controller 中 - 返回此对象:

[HttpGet]
[Route("results/{runId}")]
public async Task<IHttpActionResult> GetRunResults(int runId)
{
//Is the current cache key in our cache?
//Yes - return 304
//No - get data - and update CacheKeys
string tag = GetETag(Request);
string cacheTag = GetCacheTag("GetRunResults"); //you need to implement this map - or use Redis if multiple web servers

if (tag == cacheTag )
return new StatusCodeResult(HttpStatusCode.NotModified, Request);

//Build data, and update Cache...
string newTag = "123"; //however you define this - I have a DB auto-inc ID on my messages

//Call our new CacheableJsonResult - and assign the new cache tag
return new CacheableJsonResult<WebsiteRunResults>(results, GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings, System.Text.UTF8Encoding.Default, Request, newTag);

}
}

private static string GetETag(HttpRequestMessage request)
{
IEnumerable<string> values = null;
if (request.Headers.TryGetValues("If-None-Match", out values))
return new EntityTagHeaderValue(values.FirstOrDefault()).Tag;

return null;
}

您需要定义制作标签的粒度;我的数据是特定于用户的,因此我将 UserId 包含在 CacheKey (etag) 中

关于c# - 如何使用 Action 过滤器和 HttpResponseMessage 在 Web API 中使用 ETag,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20145140/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com