Comment charger proprement une page web sans cache ?

Comment charger proprement une page web sans cache ?

Une grande majorité des développeurs Windows Phone ont surement déjà rencontré ce problème : On souhaite requêter une page web, sauf que c’est une version provenant du cache que l’on nous retourne.

Un hack très répondu et à mon sens, pas propre, est d’ajouter un paramètre variable à l’Uri de sa requête comme par exemple :


var request = WebRequest.CreateHttp("<a href="http://www.feelmygeek.com/rand.php?s">http://www.rudyhuyn.com/?rand="+new</a> Random().Next());

ou encore


var request = WebRequest.CreateHttp("<a href="http://www.feelmygeek.com/rand.php?s">http://www.rudyhuyn.com/?rand="+DateTime.Now.Ticks</a>);

Personnellement, je ne trouve pas ça élégant, on est assez proche de MacGyver qui souhaite réparer sa voiture grâce à un trombone.

Le protocole de test

Pour effectuer mes tests, j’ai développé un petit script php:


<?php

header("Cache-Control: max-age=31536000");

header("Expires: Sun, 27 Oct 2013 14:50:11 GMT");

echo rand() . "n";

echo rand() . "n";

echo time();

?>

 

et un client Windows Phone :


var request = WebRequest.CreateHttp("http://www.feelmygeek.com/rand.php");
request.BeginGetResponse((iar) =>{
var response=request.EndGetResponse(iar).GetResponseStream();
var val = new StreamReader(response).ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(() => {
MessageBox.Show(val);
});
}, null);

 

Si deux requêtes consécutifs retourne le même résultat, on pourra donc en déduire que le cache a été utilisé.

Alors comment le faire élégamment ?

 Cache-Control :no-cache ?

Ajoutons donc  la ligne suivante à notre test :


request.Headers[HttpRequestHeader.CacheControl] = "no-cache";

Logiquement, cela devrait fonctionner, mais… non, le message affiché est bien toujours le même, il faut donc trouver une autre solution.

Cache-control : max-age=0 ?


request.Headers[HttpRequestHeader.CacheControl] = "max-age=0";

Non plus… de toute évidence, Windows Phone ignore cette instruction.

Contournons donc le problème

Vu que cache-control ne semble pas pris en compte, trouvons une autre solution.

Au lieu de dire “n’utilise pas le cache”, nous allons plutôt dire, “retourne moi une nouvelle page si elle a été modifiée depuis 1970”.

Sachant qu’internet a été inventé en 1973, on devrait être tranquille.

Voici comment le faire :


request.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.MinValue.ToString();

Note :

Si vous cherchez bien sur internet, vous trouverez probablement ce genre de solution :


request.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.UtcNow.ToString();

Cette solution est a éviter et à bannir. Pour peu que votre serveur gère correctement le header http IfModifiedSince, il vous retournera très probablement un code 304 (not modified) car littéralement vous lui demandez :

Retourne moi la page, uniquement si elle a été modifié entre maintenant et maintenant

Ce qui n’est pas le plus efficace.

Conclusion

Si vous souhaitez éviter tout problème de cache, voici donc la méthode la plus élégante pour cela :


var request = WebRequest.CreateHttp("http://www.feelmygeek.com/rand.php");
request.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.MinValue.ToString();
request.Headers[HttpRequestHeader.CacheControl] = "no-cache";
request.BeginGetResponse((iar) =>{
var response=request.EndGetResponse(iar).GetResponseStream();
var val = new StreamReader(response).ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(() => {
MessageBox.Show(val);
});
}, null);

 

Un dernier point : pourquoi avoir laissé “CacheControl : no-cache” ?

Tout simplement pour éviter que des proxys vous retourne une ancienne version de la page entre le téléphone et le serveur !

Et nos amis de Windows 8 ?

Une petite note pour nos amis WinRT, HttpWebRequest dispose d’une propriété IfModifiedSince de type DateTime, sauf que ma solution ne fonctionne pas avec eux car comme l’indique la documentation :

Si la propriété IfModifiedSince a la valeur DateTime.MinValue, l’en-tête HTTP If-Modified-Since est supprimé de la propriété Headers et de WebHeaderCollection.

La solution est donc de contourner tout cela en écrivant :


request.IfModifiedSince = DateTime.MinValue.AddSecond(1);

Voilà !

Comments are closed.