Quota localStorage atteint, gestion des erreurs

Petite introduction très rapide, le localStorage (et sessionStorage) permettent de sauvegarder des données côté client.

Les cookies font la même chose donc ? Pas tout à fait, déjà le localStorage n'est pas envoyé dans les en-têtes des requêtes http contrairement aux cookies, de plus la taille limite de sauvegarde est bien plus élevée que celle des cookies.

Tout ce qui est dit dans cet article à propos du localStorage est également vrai pour le sessionStorage.

Support des navigateurs

Après une recherche rapide sur caniuse.com on peut voir que localStorage est très bien supporté, remontant jusqu'à la version 8 d'Internet explorer.

Cependant, en regardant les known issues, on s'aperçoit que pas mal de choses pourraient ne pas fonctionner. De plus, opéra mini ne supporte simplement pas localStorage.

On peut retenir ici 2 informations importantes :

In iOS 5 & 6 localStorage data is stored in a location that may occasionally be cleared out by the OS.

In private browsing mode, Safari, iOS Safari and the Android browsers do not support setting sessionStorage or localStorage.

Sur iOS5 & 6, le localStorage peut être complètement vidé par l'OS, pas idéal si votre site/webapp fait un usage intensif du localStorage.

Le localStorage est en mode read-only en navigation privé sur les navigateurs Android, Safari, et iOS Safari. C'est pour cela qu'il faut tester l'accès au localStorage avant de l'utiliser, autrement des erreurs seront lancées.

Les différents cas possibles

1er cas : Désactivation du localStorage par l'utilisateur

Il faut prévoir le cas d'une désactivation par l'utilisateur. Si cela rend votre application complètement inutilisable il faut donc l'en informer.

Ici on pourrait penser à faire un check sur le type localStorage, par exemple :

typeof window.localStorage !== undefined

Cela va malheuresement nous retourner la même erreur que lorsqu'on essaie d'accéder au localStorage via .getItem() ou .setItem(). C'est exactement ce qu'on essaie de résoudre.

try et catch à la rescousse, pour être certain qu'on peut utiliser le localStorage de manière sure.


(function() {
    var localStorage;
    try {
        localStorage = window.localStorage;
    } catch(e) {
        console.log(e)
        // Pas d'accès au localStorage
    }
})();
    

Il faut bien faire attention à ne pas être dans le scope global sinon on écraserait window.localStorage.

En cas d'erreur le console.log nous renvoie ceci :


[
SecurityError: "The operation is insecure."
code: 18
...]

On va donc s'appuyer sur le code erreur qui est retourné, ici 18.


    (function() {
        var localStorage;
        try {
            localStorage = window.localStorage;
        } catch(e) {
            if ( e.code == 18 ) {
                // On informe l'utilisateur par exemple d'activer le localStorage
            }
        }
        if ( localStorage ) {
            // Ici on peut utiliser localStorage car on sait qu'il existe, 
            // qu'il est activé ou encore qu'il n'est pas plein
            localStorage.setItem('key','value');
        }
    })();

Dans le try on essaie d'accéder à window.localStorage ce qui génère une erreur on va donc entrer dans le catch. Notre variable localStorage est undefined si window.localStorage génère une erreur. On fait ensuite notre check à chaque fois que l'on s'apprête à utiliser le localStorage comme ceci :

2ème cas : Le localStorage est plein

Lorsque le localStorage est plein, et que la fonction setItem() est appelée une erreur de ce type sera renvoyée. :

"QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."

Comme on l'a cité dans l'introduction, cette erreur sera également présente si on est par exemple en mode privé sur Safari, Safari iOS ou encore le navigateur Android.

On va donc encore une fois utiliser un try catch et s'appuyer sur le code erreur retourné, ici : 22.


    try {
      localStorage.setItem(key, value);
    } catch(e) {
      if (e.code == 22) {
        // localstorage est plein, on informe l'utilisateur
      }
    }

Solution finale

On ajoute alors cette dernière vérification dans le code que l'on a écrit précédemment ce qui donnera ceci :


    (function() {
        var localStorage;
        try {
            // Ici on va tenter d'écrire dans le localStorage
            // pour être sur que le localStorage n'est pas en mode read-only 
            // ou plein
            window.localStorage.setItem('LocalStorageOK','1');
            window.localStorage.removeItem('LocalStorageOK');
            localStorage = window.localStorage;
        } catch(e) {
            if ( e.code == 18 ) {
                // On informe l'utilisateur par exemple d'activer le localStorage
            }
            if (e.code == 22) {
                // localStorage est plein ou bien l'utilisateur est en mode privé
                // sur Safari, Safari iOS, ou le navigateur Android 
            }
        }
        if ( localStorage ) {
            // Ici on peut utiliser localStorage car on sait qu'il existe, 
            // qu'il est activé ou encore qu'il n'est pas plein
            localStorage.setItem('key','value');
        }
    })();

Une fois le try catch mis en place, il faut bien penser à toujours englober ses appels au localStorage par ceci :

if ( localStorage ) { 
    // Notre code utilisant setItem ou getItem 
}

Cette solution devrait couvrir tous les cas d'erreurs liés au localStorage, et est assez simple à mettre en place en début de projet.

comments powered by Disqus