简体   繁体   中英

Check something synchronous before every Backbone HTTP request

In every authenticated requests (GET, POST, etc) of my Backbone/Marionette application I must to attach an accessToken.

I store this accessToken and expireDate in the localStorage.

To check if the accessToken is expired I call this method: user.checkToken(). If is expired, the method renew the accessToken with a POST request to my backend.

Where should I put this check? I mean, in which part of the application?

Should I rewrite my on Backbone.sync method or use ajax.setup "beforeSend" ?

Thanks in advance for your advices/idea.

Overwrite your model's sync() function and do whatever you need to do.. Something like:

var MyModel = Backbone.Model.extend({
  sync: function() {
    // Put your code here

    Backbone.Model.prototype.sync.apply(this, arguments);
  }
});

Edit #1:

Not sure where you get user (as well as other variables) from but here it is:

var MyModel = Backbone.Model.extend({
  sync: function() {
    user.checkToken().done(_.bind(function(){
       Backbone.Model.prototype.sync.apply(this, [ method, model, options ]);
     });
  }, this);
});

Backbone uses jQuery (see the note for a solution that may work with Zepto) for ajax requests, so you can use (as suggested by Edward) jQuery.ajaxPrefilter .

I did a little test for this task, let me know if there's any problem:

function tokenIsExpired() {
    return true;
}

function createPromiseFunction(method, jqXHRsource, jqXHR) {
    return function() {
        jqXHRsource[method] = function(f) {
            if (f) {
                jqXHR[method] = function() {
                    f.apply(this, arguments);
                };
            }

            return this;
        };
    }
}

function updateToken() {
    return $.ajax({
        url: '',
        method: 'GET',
        data: {some:'data', here:'yes'},
        success: function() {
            // update the token sir
            console.log('token call done')
        },
        skipTokenCheck: true // required
    });
}


$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
    /*
     * check if token is expired every time a new ajax request is made
     * if it is expired, aborts the current requests, updated the token
     * and eventually does the original request again.
     */

    if (!options.skipTokenCheck && tokenIsExpired()) {
        // at this point no callback should have be added to the promise object

        var methodsNames = [
            'done',
            'always',
            'fail',
            'progress',
            'then'
        ];

        var methods = {};

        // copy the callbacks when they're added to the old request

        for (var i = 0; i < methodsNames.length; i++) {
            var name = methodsNames[i];

            createPromiseFunction(name, jqXHR, methods)();
        };

        jqXHR.abort();

        // TODO: error checks
        updateToken().done(function() {
            console.log('done');
            var newReq = $.ajax($.extend(originalOptions, {skipTokenCheck: true}));

            for (var i = 0; i < methodsNames.length; i++) {
                var name = methodsNames[i];
                var f = methods[name];

                if (f) {
                    newReq[name](f);
                }
            };
        });
    }
});

var p = $.get('.');

p.done(function() { console.log(arguments); }).fail(function() {
    console.log('fail');
});

Looks like that ajaxPrefilter doesn't work with Zepto. Alternatively you can use the ajaxBeforeSend event.

Returning false in the beforeSend function will cancel the request.

Should be easy to adapt the code I posted above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM