Caching with AngularJs

2019-09-04 06:46发布

Currently I'm trying to cache a user's information. I give you guys a scenario of what is happening.

a user is trying to log in with an account A. Account A's name appears on the navbar. After that user log out and tries to log in with an account B, on the navbar itself the name is still belongs to account A's name.

The code

service.js

.factory('Auth', function($http, $q, AuthToken) {

    // create auth factory object
    var authFactory = {};

    // log a user in
    authFactory.login = function(username, password) {

        // return the promise object and its data
        return $http.post('/api/login', {
            username: username,
            password: password
        })
        .success(function(data) {
            AuthToken.setToken(data.token);
            return data;
        });
    };

    // log a user out by clearing the token
    authFactory.logout = function() {
        // clear the token
        AuthToken.setToken();
    };

    // check if a user is logged in
    // checks if there is a local token
    authFactory.isLoggedIn = function() {
        if (AuthToken.getToken()) 
            return true;
        else
            return false;   
    };

    // get the logged in user
    authFactory.getUser = function() {
        if (AuthToken.getToken())
            return $http.get('/api/me', {cache: true});
        else
            return $q.reject({ message: 'User has no token.' });        
    };

    // return auth factory object
    return authFactory;

})

// ===================================================
// factory for handling tokens
// inject $window to store token client-side
// ===================================================
.factory('AuthToken', function($window) {

    var authTokenFactory = {};

    // get the token out of local storage
    authTokenFactory.getToken = function() {
        return $window.localStorage.getItem('token');
    };

    // function to set token or clear token
    // if a token is passed, set the token
    // if there is no token, clear it from local storage
    authTokenFactory.setToken = function(token) {
        if (token)
            $window.localStorage.setItem('token', token);
        else
            $window.localStorage.removeItem('token');
    };

    return authTokenFactory;

})

// ===================================================
// application configuration to integrate token into requests
// ===================================================
.factory('AuthInterceptor', function($q, $location, AuthToken) {

    var interceptorFactory = {};

    // this will happen on all HTTP requests
    interceptorFactory.request = function(config) {

        // grab the token
        var token = AuthToken.getToken();

        // if the token exists, add it to the header as x-access-token
        if (token) 
            config.headers['x-access-token'] = token;

        return config;
    };

    // happens on response errors
    interceptorFactory.responseError = function(response) {

        // if our server returns a 403 forbidden response
        if (response.status == 403)
            $location.path('/login');

        // return the errors from the server as a promise
        return $q.reject(response);
    };

    return interceptorFactory;

});

controller.js

angular.module('mainCtrl', [])

.controller('MainController', function($rootScope, $location, Auth) {

    var vm = this;

    // get info if a person is logged in
    vm.loggedIn = Auth.isLoggedIn();

    // check to see if a user is logged in on every request
    $rootScope.$on('$routeChangeStart', function() {
        vm.loggedIn = Auth.isLoggedIn();    

        // get user information on page load
        Auth.getUser()
            .then(function(data) {
                vm.user = data.data;
            }); 
    }); 

    // function to handle login form
    vm.doLogin = function() {
        vm.processing = true;

        // clear the error
        vm.error = '';

        Auth.login(vm.loginData.username, vm.loginData.password)
            .success(function(data) {
                vm.processing = false;

                // get user information on page load
                Auth.getUser()
                    .then(function(data) {
                        vm.user = data.data;
                    });

                // if a user successfully logs in, redirect to users page
                if (data.success) 
                    $location.path('/');
                else
                    vm.error = data.message;
            });
    };

    // function to handle logging out
    vm.doLogout = function() {
        Auth.logout();
        $location.path('/logout');
    };

});

index.html

<ul class="nav navbar-nav navbar-right">
            <li ng-if="!main.loggedIn"><a href="/login">Login</a></li>
            <li ng-if="main.loggedIn"><a href="#">Hello {{ main.user.username }}</a></li>
            <li ng-if="main.loggedIn"><a href="#" ng-click="main.doLogout()">Logout</a></li>
            <li><a href=""><button class="btn btn-primary">Write</button></a></li>
        </ul>

So basically my assumption of the problem lies in the service.js where i added cache: true. Do i need to add some logic to it?

1条回答
何必那么认真
2楼-- · 2019-09-04 07:11

There are two different caches that may be involved in your app. First it's the angular cache which you have set below {cache: true}

authFactory.getUser = function() {
    if (AuthToken.getToken())
        return $http.get('/api/me', {cache: true});
    else
        return $q.reject({ message: 'User has no token.' });        
};

This cache is only there for the duration of the app, once you leave or reload the page, it's gone!

The other cache which is the browser cache is a little more complex to deal with. Note this has no relationship with the Angular cache, so if this is your problem simply turning off {cache: false} wont help. To prevent cache you will need to send a list of different caching headers in your restful API and it may not always work.

The easiest way to prevent cache is to add a version to your url which doesnt actually affect your results but tricks your browser into thinking that it's a different url. This is referred to as Cache Busting.

The easiest way to cache bust is to add a Math.Random() to append to the url. The chances of Math.Random to be the same is probably in the billions.

authFactory.getUser = function() {
    if (AuthToken.getToken())
        return $http.get('/api/me?rand=' + Math.random(), {cache: true});
    else
        return $q.reject({ message: 'User has no token.' });        
};

However, if you want a better way to do it specific for your app, you could append the username to your url. This way it will cache for the same users which means you are actually taking advantage of the caching mechanism and not getting tied down by it!

authFactory.getUser = function() {
    if (AuthToken.getToken())
        return $http.get('/api/me?user=' + <username>, {cache: true});
    else
        return $q.reject({ message: 'User has no token.' });        
};
查看更多
登录 后发表回答