Comment utiliser ETag dans l'API Web en utilisant une action de filtre avec HttpResponseMessage
J'ai un ASP.Net Web API contrôleur qui renvoie la liste des utilisateurs.
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;
}
}
et une action de filtre d'attribut de classe EnableTag
qui est responsable de la gestion de l'ETag et cache :
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();
}
}
Le code ci-dessus créer un attribut de classe pour gérer l'ETag. Donc sur la première demande, il va créer une nouvelle adresse E-Tag et de la demande ultérieure, il va vérifier si tout ETag est existé. si oui, il va générer Not Modified
d'État HTTP et le retourner au client.
Mon problème est que je veux créer une nouvelle ETag si il y a des changements dans ma liste d'Utilisateurs, ex. un nouvel utilisateur est ajouté, ou un utilisateur existant est supprimé. et l'ajouter à la réponse. Cela peut être suivi par le userList
variable.
Actuellement, l'ETag reçues entre le client et le serveur sont les mêmes de chaque 2e demande, dans ce cas, il sera toujours générer des Not Modified
statut, alors que je veux qu'elle alors qu'en réalité, rien n'a changé.
Quelqu'un peut-il me guider dans cette direction? Merci à l'avance.
OriginalL'auteur SoftSan | 2013-11-22
Vous devez vous connecter pour publier un commentaire.
Mon exigence était de cache de mon site web api JSON réponses... Et de toutes les solutions proposées n'ont pas un simple "lien" à l'endroit où les données sont générées - à-dire dans le Contrôleur...
Donc ma solution a été de créer un wrapper "CacheableJsonResult" qui a généré une Réponse, et ensuite ajouté l'ETag à l'en-tête. Cela permet à un etag être transmises lors de la méthode de contrôleur est généré et veut retourner le contenu...
Et puis, dans votre contrôleur - retour cet objet:
Vous avez besoin pour définir la façon granulaire pour faire vos balises; mes données spécifiques à l'utilisateur, j'ai donc inclure le nom d'utilisateur dans le CacheKey (etag)
OriginalL'auteur James Joyce
une bonne solution pour ETag et dans ASP.NET l'API Web est d'utiliser CacheCow . Un bon article est ici.
Il est facile à utiliser et vous n'avez pas à créer un Attribut personnalisé.
Amusez-vous
.u
OriginalL'auteur imperugo
J'ai trouvé CacheCow très lourd pour ce qu'il fait, si la seule raison est, pour diminuer la quantité de données transférées, vous pouvez utiliser quelque chose comme ceci:
Pas besoin de configurer quoi que ce soit, et oublier. La façon dont je l'aime. 🙂
async n'existait pas à l'époque, mais ressemble à un grand plus.
OriginalL'auteur Viezevingertjes
J'aime la réponse qui a été fournie par @Viezevingertjes. C'est le plus élégant et le "Pas besoin de configurer quoi que ce soit" l'approche est très pratique. Je l'aime trop 🙂
Cependant, je pense qu'il a quelques inconvénients:
Aussi il ne faisait pas partie de la question et personne n'a mentionné. Mais ETag doit être utilisé pour la validation du Cache. Par conséquent, il doit être utilisé avec-tête Cache-Control afin que les clients n'ont même pas à appeler le serveur jusqu'à ce que le cache expire (il peut être très courte période de temps dépend de vos ressources). Lorsque le cache expiré, le client effectue une demande auprès de l'ETag et de les valider. Pour plus de détails sur la mise en cache voir cet article.
C'est pourquoi j'ai décidé de customiser un peu, mais. Simplifié filtre pas besoin de OnActionExecuting méthode fonctionne avec Tous les types de réponse, pas de Sérialisation. Et, surtout, ajoute CacheControl en-tête ainsi. Elle peut être améliorée par exemple avec le service Public de cache activé, etc... Cependant, je vous conseille fortement de comprendre la mise en cache et de le modifier avec soin. Si vous utilisez le protocole HTTPS et les points de terminaison sont garantis puis cette configuration doit être fine.
Utilisation de l'e.g: avec 1 min de mise en cache côté client:
OriginalL'auteur Major
Semble être un bon moyen de le faire:
OriginalL'auteur Andre L.A.C Bittencourt