jQuery ajax generic error handling and on a case-b

2019-02-02 14:26发布

问题:

I have a generic ajax Error handler written like so:

$('html').ajaxError(function(e, xhr, settings, exception) {

    var message = '';

    if (xhr.status == 0) {
        message = 'You are offline!\n Please check your network.';
    }
    else if (xhr.status == 403) {
        window.location.href = $('#logon').attr('href');
    }
    else if (xhr.status == 404) {
        message = 'Requested URL not found.';
    }
    else if (xhr.status == 500) {

        message = xhr.responseText;

        $('#cboxLoadedContent div.news_article_content').append('<p>' + message + '</p>');

        try {//Error handling for POST calls
            message = JSON.parse(xhr.responseText);
        }

        catch (ex) {//Error handling for GET calls
            message = xhr.responseText;
        }

    }
    else if (errStatus == 'parsererror') {
        message = 'Error.\nParsing JSON Request failed.';

    }
    else if (errStatus == 'timeout') {
        message = 'Request timed out.\nPlease try later';
    }
    else {
        message = ('Unknown Error.\n' + xhr.responseText);
    }

    if (message != '' && xhr.status != 500) {
        message = message;
    }

    if (xhr.status != 403) {

        $('#icis_dashboard').append('<p id="ajax_error_msg" class="offScreen">' + message + '</p>');

        errorBox({
            inline: true,
            width: 0,
            href: '#ajax_error_msg',
            onLoadCall: function() { $('#cboxLoadedContent').jScrollPaneRemove(); },
            onCleanupCall: function() { $('#ajax_error_msg').remove(); }
        });
    }

});

So when the error is not 403 a dialog is shown with text relating to the error.

This is fine but what iwould like to do is have the generic handler as a backup and then deal with each error on an individual basis within the original ajax call.

so as the backup handler alerts "bar" on a 404 i would like to alert "foo" instead:

            error: function(xhr) {
            if (xhr.status == 404) {
                //window.location.href = $('#logon').attr('href');
                alert("foo");    
            }
        }

I sthere anyway to do this? I don't know how to prevent the backup from firing as they both seem to trigger at the moment.

回答1:

I don't think you can control this with jQuery. The global ajaxError gets called on any error that happens during an ajax call. However, the "local" error callback gets called before the global callback so you could set a variable that tells the global callback not to run.

For instance:

var handledLocally = false;

$('html').ajaxError(function(e, xhr, settings, exception) {
    if (!handledLocally){
        //run the normal error callback code and the reset handledLocally
    }
});

error: function(){
    //set handledLocally to true to let the global callback it has been taken care of
    handledLocally = true;
}

You can view this jsFiddle that shows how this can be accomplished (be sure to click run at the top before clicking the links): http://jsfiddle.net/e7By8/



回答2:

You can set the ajax property "global" to false in the ajax function that does it's own error handling. That should prevent global error handling. Check: http://api.jquery.com/jQuery.ajax#toptions.



回答3:

I think set global variable is a bad idea because you can have multiple ajax request at same time. As was mentioned "local" error callback gets called before the global callback. My solution is just attach some custom property to jqXHR object for example handledLocally and set it true in your local handler and check it in global handler.

local handling:

$.ajax({
   //some settings
   error:function(jqXHR, textStatus, errorThrown){
      //handle and after set
      jqXHR.handledLocally = true

   }
})

global handling:

$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
    if (jqXHR.handledLocally)) {
        return;
    }
    //handle global error
})


回答4:

We actually solved this with the following:

function ajaxError(xhr, textStatus, errorThrown) {

    var message = '';

    if (xhr.status == 0) {
        message = 'You are offline!\n Please check your network.';
    }
    else if (xhr.status == 403) {
        window.location.href = $('#logon').attr('href');
    }
    else if (xhr.status == 404) {
        message = 'Requested URL not found.';
    }
    else if (xhr.status == 500) {

        message = xhr.responseText;

        $('#cboxLoadedContent div.news_article_content').append('<p>' + message + '</p>');

        try {//Error handling for POST calls
            message = JSON.parse(xhr.responseText);
        }

        catch (ex) {//Error handling for GET calls
            message = xhr.responseText;
        }

    }
    else if (errStatus == 'parsererror') {
        message = 'Error.\nParsing JSON Request failed.';

    }
    else if (errStatus == 'timeout') {
        message = 'Request timed out.\nPlease try later';
    }
    else {
        message = ('Unknown Error.\n' + xhr.responseText);
    }

    if (message != '' && xhr.status != 500) {
        message = message;
    }

    if (xhr.status != 403) {

        $('#icis_dashboard').append('<p id="ajax_error_msg" class="offScreen">' + message + '</p>');

        errorBox({
            inline: true,
            width: 0,
            href: '#ajax_error_msg',
            onLoadCall: function() { $('#cboxLoadedContent').jScrollPaneRemove(); },
            onCleanupCall: function() { $('#ajax_error_msg').remove(); }
        });
    } 
};

So every AJAX call will now reference the funciton in one of two ways:

1) if we want the default to happen

error: ajaxError

2) if we want to target a specific situation, if that is not caught then proceed with the default

    error: function(xhr) {

        if (xhr.status == 404) {
            alert("local");
        }
        else {
            // revert to default
            ajaxError.apply(this, arguments);
        }
    }


回答5:

It is an old question, but this may be usefull for someone. The problem with this answer is that you may have code in the global handlers that you need to be executed. I found this other answer more usefull but it's not a very good practice to use global variables. Also you might have more than one ajax call at the same time and it wont work. I propose this alternative slightly different:

$(document).ajaxError(function(event, jqxhr, settings, thrownError) {
    // Global validation like this one:
    if (jqxhr.status == 401) { window.location = "/login"; }

    if (!settings.handErrorLocally) {
        // Unhandled error.
        alert("Ooops... we have no idea what just happened");
    }
});

And then when calling any ajax you might handle the error yourself:

$.ajax({
    url: "/somePage/",
    handErrorLocally: true
}).done(function () {
    alert("good");
}).fail(function () {
    alert("fail");
});

Or let the global handler take care:

$.ajax({
    url: "/somePage/"
}).done(function () {
    alert("good");
});


回答6:

Define global error handler as function

function ajaxError() {
  var message = '';

  if (xhr.status == 0) {
    message = 'You are offline!\n Please check your network.';
  }
  .....
}

And create jQuery.ajax wrapper:

function doAjax(url, fnError) {
  jQuery.ajax(url, {
    error: fnError || defaultErrorHandler 
  })
}

Now use this wrapper instead default jQuery.ajax:

doAjax('/someResource'); // defaultErrorHandler using for handle error
doAjax('/someResource', function() {...}); // custom error function using


回答7:

I am adding to the answer by Tim Banks which was extremely useful. I just want to change the fact that he uses a global variable and use a setting in the ajax call. I have made it default to falling back but this could easily be changed.

$('html').ajaxError(function(e, xhr, settings, exception) {
    if (xhr.status == 404) {
        alert("html error callback");    
    }
});

$.ajax({
    url: "missing.html",
    global: true
});

I have edited my answer to use the global setting which decides whether to propogate to the global event handler