Global error handler for backbone.js ajax requests

16,596

Solution 1

Use jQuery directly for this.

$(document).ajaxError(function (e, xhr, options) {
  // do your stuff
});

You can do the same thing for CSRF in rails for exemple (using ajaxSend).

You can read more here: http://api.jquery.com/jQuery.ajax#advanced-options

Solution 2

Backbone's sync triggers an 'error' event when errors occur. So one approach you could take is to extend Backbone's Model and Collection objects to add these add-on error checks. It would look something like this:

ErrorHandlingModel = Backbone.Model.extend({

    initialize: function(attributes, options) {
        options || (options = {});
        this.bind("error", this.defaultErrorHandler);
        this.init && this.init(attributes, options);
    },

    defaultErrorHandler: function(model, error) {
        if (error.status == 401 || error.status == 403) {
            // trigger event or route to login here.
        }
    }

});

OtherModel = ErrorHandlingModel.extend({
});

and you would do something similar for the Collection object. I haven't tested the above, but think its pretty close. Obviously, you would choose better class names. The init method just enables subclasses to get a chance to do their own initialization.

Solution 3

What I found is possibly the "most right way" in Backbone:

var GeneralErrorView = Backbone.View.extend({
  events: {
    'ajaxError': 'handleAjaxError'
  },
  handleAjaxError: function (event, request, settings, thrownError) {
    //...handling goes here
  }
});

this.view = GeneralErrorView({el: document});

You can put any error handling logic without extending Models or Collections. Make use of Backbone.Events inside handler and transmit messages to other error handlers or something like that.

Solution 4

You can handle this via the jQuery .ajaxSetup method. We have an identical situation (backbone app which may get a 401 error at any time) and we handle it by using jQuery's ajaxSetup in the entry point of our app:

var appView = new AppView(options);

$.ajaxSetup({
    cache: false,
    statusCode: {
        401: function () {
            appView.signIn();
        }
    }
});

appView.render();

Backbone.history.start({ root: config.rootUrl, pushState: true });

This approach gives global error handling without the need to extend the Backbone base classes.

Share:
16,596
Plastic Rabbit
Author by

Plastic Rabbit

Updated on June 12, 2022

Comments

  • Plastic Rabbit
    Plastic Rabbit almost 2 years

    Is there way to bind one error handler for ajax requests that performs by backbone.js?

    My situation: I can get 401 (Unauthorized) at any time, so I need to show login popup.

  • Ben
    Ben about 12 years
    Should a model really be tasked to handle a 401 Unauthorized?
  • Bill Eisenhauer
    Bill Eisenhauer about 12 years
    Good question. We did so purely because models and collections were our conduit to the server. It doesn't seem like a responsibility traditionally assigned to a model-like thing, so maybe there's a better way. Back in May, there wasn't much prior art to reflect on, so this is the approach we came up with.
  • cerberos
    cerberos about 11 years
    Note that this callback will execute for all jQuery ajax errors, not just those related to backbone requests. There may be other libraries in the page making ajax requests via jquery, you probably don't want the callback in those cases.
  • Harry
    Harry almost 11 years
    This looks like a good approach to me, I'm not a Backbone expert though. Would this handle errors that occur when fetching a collection of models?
  • Nilesh Kale
    Nilesh Kale over 10 years
    This approach of using global ajaxError callback does not work with Backbone models/collections if the fetch, sync, create methods have a error callback provided as the option.
  • challet
    challet about 10 years
    -1 this is a global error handler for jquery ajax requests, not for backbone. It might actually use an other sync channel.
  • Ant Swift
    Ant Swift almost 10 years
    @HarryGordon, this method will work for Model sync operations only. A similar base class will be required for Collections.
  • Ant Swift
    Ant Swift almost 10 years
    @Ben, an approach I've used is to raise an event on the error with Backbone.trigger('login-failed');. This can be picked up by the app and handled outside the model.
  • Henry Wilson
    Henry Wilson almost 10 years
    I really don't think extending models/collections is the right way to handle 401 errors. See my answer below for a different approach. If you're hell bent on extending anything in Backbone it should be the View IMO, but you don't need to extend the base classes to handle a 401.
  • Bill Eisenhauer
    Bill Eisenhauer almost 10 years
    Hey @HenryWilson, I agree with you now. At the time, with deadlines approaching and not much help really out there, this solution worked for us. I concede as such in my comment from Feb '12 above.
  • Edwin Stoteler
    Edwin Stoteler over 9 years
    This works, however the request, settings, thrownError parameters are all undefined when I got a 500 error from the server. I can't really use this solution if I don't got information about the error. Did I do something wrong?
  • Vytautas Butkus
    Vytautas Butkus over 9 years
    This might be outdated because I used this almost two years ago, you need to check is there anything passed to callback at all
  • Craig Brett
    Craig Brett about 9 years
    This approach seems alright, rather than completely upending all your models to come off of some hand-rolled auth-error-handling model, you just catch it in one place. Much simpler.
  • user625488
    user625488 almost 9 years
    Would this work without extending backbone's views/models to some generic new base classes, but instead calling Backbone.Model.prototype.defaultErrorHandler = function() {...}, and replacing the constructors for models, views and collections too, so they attach the default error handler?
  • Bill Eisenhauer
    Bill Eisenhauer almost 9 years
    Seems plausible, but then you have to be mindful to follow those steps for every new type of thing that you add to your app. We favored an approach where we just baked it all in. Its been nearly 4 years since I posted this answer, so I would guess there's better insight out there to be had.