How to display a JSON error message?

Tiny Giant Studios picture Tiny Giant Studios · Jan 7, 2011 · Viewed 15.5k times · Source

I'm currently developing a tumblr theme and have built a jQuery JSON thingamabob that uses the Tumblr API to do the following:

The user would click on the "post type" link (e.g. Video Posts), at which stage jQuery would use JSON to grab all the posts that's related to that type and then dynamically display them in a designated area.

Now everything works absolutely peachy, except that with Tumblr being Tumblr and their servers taking a knock every now and then, the Tumblr API thingy is sometimes offline. Now I can't foresee when this function will be down, which is why I want to display some generic error message if JSON (for whatever reason) was unable to load the post.

You'll see I've already written some code to show an error message when jQuery can't find any posts related to that post type BUT it doesn't cover any server errors. Note: I sometimes get this error:

Failed to load resource: the server responded with a status of 503 (Service Temporarily Unavailable)

It is for this 503 Error message that I need to write some code, but I'm slightly clueless :)

Here's the jQuery JSON code:

$('ul.right li').find('a').click(function() {
  var postType = this.className;
  var count = 0;
  byCategory(postType);
  return false;

  function byCategory(postType, callback) {
    $.getJSON('{URL}/api/read/json?type=' + postType + '&callback=?', function(data) {
    var article = [];
     $.each(data.posts, function(i, item) {
     // i = index
     // item = data for a particular post
     switch(item.type) {
     case 'photo':
     article[i] = '<div class="post_wrap"><div class="photo" style="padding-bottom:5px;">'
         + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/XSTldh6ds/photo_icon.png" alt="type_icon"/></a>'
         + '<a href="' + item.url + '" title="{Title}"><img src="' 
       + item['photo-url-500'] 
       + '"alt="image" /></a></div></div>';
     count = 1;
     break;
     case 'video':
     article[i] = '<div class="post_wrap"><div class="video" style="padding-bottom:5px;">'
         + '<a href="' + item.url + '" title="{Title}" class="type_icon">'
       + '<img src="http://static.tumblr.com/ewjv7ap/nuSldhclv/video_icon.png" alt="type_icon"/></a>'
         + '<span style="margin: auto;">' 
       + item['video-player'] 
       + '</span>' 
       + '</div></div>';
     count = 1;
     break;
     case 'audio':
     if (use_IE == true) {
     article[i] = '<div class="post_wrap"><div class="regular">'
             + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/R50ldh5uj/audio_icon.png" alt="type_icon"/></a>'
         + '<h3><a href="'
         + item.url
       + '">'
       + item['id3-artist'] 
       +' - '
       + item['id3-title']
       + '</a></h3>'
       + '</div></div>';

    } else {
     article[i] = '<div class="post_wrap"><div class="regular">'
             + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/R50ldh5uj/audio_icon.png" alt="type_icon"/></a>'
       + '<h3><a href="'
         + item.url
       + '">'
       + item['id3-artist'] 
       +' - '
       + item['id3-title']
       + '</a></h3><div class="player">'
       + item['audio-player'] 
       + '</div>'
       + '</div></div>';
    };
     count = 1;
     break;
     case 'regular':
     article[i] = '<div class="post_wrap"><div class="regular">' 
       + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/dwxldhck1/regular_icon.png" alt="type_icon"/></a><h3><a href="'
       + item.url 
       + '">' 
       + item['regular-title']
       + '</a></h3><div class="description_container">'
       + item['regular-body'] 
       + '</div></div></div>';
     count = 1;
     break;
     case 'quote':
     article[i] = '<div class="post_wrap"><div class="quote">'
         + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/loEldhcpr/quote_icon.png" alt="type_icon"/></a><blockquote><h3><a href="' + item.url + '" title="{Title}">' 
       + item['quote-text']
       + '</a></h3></blockquote><cite>- '
       + item['quote-source'] 
       + '</cite></div></div>';
     count = 1;
     break;
     case 'conversation':
     article[i] = '<div class="post_wrap"><div class="chat">' 
       + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/MVuldhcth/conversation_icon.png" alt="type_icon"/></a><h3><a href="' 
       + item.url 
       + '">'
       + item['conversation-title']
       + '</a></h3></div></div>';
     count = 1;
     break;
     case 'link':
     article[i] = '<div class="post_wrap"><div class="link">' 
       + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/EQGldhc30/link_icon.png" alt="type_icon"/></a><h3><a href="'
       + item['link-url'] 
       + '" target="_blank">'
       + item['link-text']
       + '</a></h3></div></div>';
     count = 1;
     break;
     default:
     alert('No Entries Found.');
     };
     }) // end each

     if (!(count == 0)) {
     $('#content_right')
      .hide('fast')
      .html('<div class="first_div"><span class="left_corner"></span><span class="right_corner"></span><h2>Displaying ' 
      + postType 
      + ' Posts Only</h2></div>'
      + article.join(''))
    .slideDown('fast')
    } else {
     $('#content_right')
     .hide('fast')
     .html('<div class="first_div"><span class="left_corner"></span><span class="right_corner"></span><h2>Hmmm, currently there are no ' 
       + postType 
       + ' posts to display</h2></div>')
     .slideDown('fast')
    }


    // end getJSON
   }); // end byCategory
  }
 });

If you'd like to see the demo in action, check out Elegantem but do note that everything might work absolutely fine for you (or not), depending on Tumblr's temperament.


Update Okay, so after following jmorts answer underneath as close to the letter as 2am allows, I've churned out the following code without success - there's no alert popping up. Myabe I'm a muppet, maybe I'm just scheleeeepy but if you jedi folks can take another peek I'd really appreciate it :)

$('ul.right li').find('a').click(function() {
        var postType = this.className;
        var count = 0;
        byCategory(postType);
        return false;

        function byCategory(postType, callback) {
          $.getJSON('{URL}/api/read/json?type=' + postType + '&callback=?', function(data, textStatus, xhr) { // main callback function
          if(xhr.status == 500 || xhr.status == 404 || xhr.status == 503) {
                  yourErrorHandler(data, textStatus, xhr); // success
                } else {
                  yourCallbackToRunIfSuccessful(data);   // failed
                }
              }
        );


        function yourCallbackToRunIfSuccessful(data) {  
          var article = [];
              $.each(data.posts, function(i, item) {
              // i = index
              // item = data for a particular post
              switch(item.type) {
              case 'photo':
              article[i] = '<div class="post_wrap"><div class="photo" style="padding-bottom:5px;">'
                            + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/XSTldh6ds/photo_icon.png" alt="type_icon"/></a>'
                            + '<a href="' + item.url + '" title="{Title}"><img src="' 
                            + item['photo-url-500'] 
                            + '"alt="image" /></a></div></div>';
              count = 1;
              break;
              case 'video':
              article[i] = '<div class="post_wrap"><div class="video" style="padding-bottom:5px;">'
                            + '<a href="' + item.url + '" title="{Title}" class="type_icon">'
                            + '<img src="http://static.tumblr.com/ewjv7ap/nuSldhclv/video_icon.png" alt="type_icon"/></a>'
                            + '<span style="margin: auto;">' 
                            + item['video-player'] 
                            + '</span>' 
                            + '</div></div>';
              count = 1;
              break;
              case 'audio':
              if (use_IE == true) {
              article[i] = '<div class="post_wrap"><div class="regular">'
                            + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/R50ldh5uj/audio_icon.png" alt="type_icon"/></a>'
                            + '<h3><a href="'
                            + item.url
                            + '">'
                            + item['id3-artist'] 
                            +' - '
                            + item['id3-title']
                            + '</a></h3>'
                            + '</div></div>';

                } else {
              article[i] = '<div class="post_wrap"><div class="regular">'
                            + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/R50ldh5uj/audio_icon.png" alt="type_icon"/></a>'
                            + '<h3><a href="'
                            + item.url
                            + '">'
                            + item['id3-artist'] 
                            +' - '
                            + item['id3-title']
                            + '</a></h3><div class="player">'
                            + item['audio-player'] 
                            + '</div>'
                            + '</div></div>';
                };
              count = 1;
              break;
              case 'regular':
              article[i] = '<div class="post_wrap"><div class="regular">' 
                            + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/dwxldhck1/regular_icon.png" alt="type_icon"/></a><h3><a href="'
                            + item.url 
                            + '">' 
                            + item['regular-title']
                            + '</a></h3><div class="description_container">'
                            + item['regular-body'] 
                            + '</div></div></div>';
              count = 1;
              break;
              case 'quote':
              article[i] = '<div class="post_wrap"><div class="quote">'
                            + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/loEldhcpr/quote_icon.png" alt="type_icon"/></a><blockquote><h3><a href="' + item.url + '" title="{Title}">' 
                            + item['quote-text']
                            + '</a></h3></blockquote><cite>- '
                            + item['quote-source'] 
                            + '</cite></div></div>';
              count = 1;
              break;
              case 'conversation':
              article[i] = '<div class="post_wrap"><div class="chat">' 
                            + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/MVuldhcth/conversation_icon.png" alt="type_icon"/></a><h3><a href="' 
                            + item.url 
                            + '">'
                            + item['conversation-title']
                            + '</a></h3></div></div>';
              count = 1;
              break;
              case 'link':
              article[i] = '<div class="post_wrap"><div class="link">' 
                            + '<a href="' + item.url + '" title="{Title}" class="type_icon"><img src="http://static.tumblr.com/ewjv7ap/EQGldhc30/link_icon.png" alt="type_icon"/></a><h3><a href="'
                            + item['link-url'] 
                            + '" target="_blank">'
                            + item['link-text']
                            + '</a></h3></div></div>';
              count = 1;
              break;
              default:
              alert('No Entries Found.');
              };
              }) // end each

              if (!(count == 0)) {
              $('#content_right')
                .hide('fast')
                .html('<div class="first_div"><span class="left_corner"></span><span class="right_corner"></span><h2>Displaying ' 
                  + postType 
                  + ' Posts Only</h2></div>'
                  + article.join(''))
                .slideDown('fast')
                } else {
                    $('#content_right')
                    .hide('fast')
                    .html('<div class="first_div"><span class="left_corner"></span><span class="right_corner"></span><h2>Hmmm, currently there are no ' 
                      + postType 
                      + ' posts to display</h2></div>')
                    .slideDown('fast')
                }


                // end getJSON
            }; // end byCategory

            function yourErrorHandler(data,textStatus,xhr) {
                alert("Server returned status code " + xhr.status + ".  Try again later.");
            }
        }
    });

Answer

jmort253 picture jmort253 · Jan 7, 2011

Your callback actually takes 2 other parameters you're not showing:

     $.getJSON('{URL}/api/read/json?type=' + postType + 
          '&callback=?', 
              function(data, textStatus, xhr) {   // main callback function
                if(xhr.status == 500 || xhr.status == 404 || xhr.status == 503) {
                  yourErrorHandler(data, textStatus, xhr); // success
                } else {
                  yourCallbackToRunIfSuccessful(data);   // failed
                }
              }
       );

       // your original code, but wrapped up in it's own function definition
       function yourCallbackToRunIfSuccessful(data) {
         var article = [];
         $.each(data.posts, function(i, item) {
         // i = index
         // item = data for a particular post
         switch(item.type) {
           case 'photo':
           ...
           ...
       }

       function yourErrorHandler(data,textStatus,xhr) {
           alert("Server returned status code " + xhr.status + ".  Try again later.");
       }

You can use the xhr object to check the status of the raw XMLHttpRequest object. If you get a 404, 503, 500, etc then you can display your error message or run your alternate function.

http://api.jquery.com/jQuery.ajax

Also, if you don't already have Firebug for Firefox, I'd highly recommend it for JavaScript debugging: http://getfirebug.com/

UPDATE:

The getJSON JQuery AJAX wrapper does not have an error callback handler. Instead, you'll need to use the regular JQuery AJAX handler to make your JSON request:

  jQuery.ajax({
     type: "GET",
     url: '{URL}/api/read/json?type=' + postType + 
          '&callback=?',
     dataType: "json",
     success: function(results){
         console.info("Success!");
         yourCallbackToRunIfSuccessful(results);
     },
     error: function(XMLHttpRequest, textStatus, errorThrown){
         alert("Error");
         yourErrorHandler(XMLHttpRequest, textStatus, errorThrown);
     }
  });

It's important to note that this is NOT JSONP. This means you cannot make cross-domain requests using this function.

If you're relying on JSONP using my original answer, then you'll need to implement a solution whereby you set a setInterval event to poll a value that will be changed in your callback. More details can be found here:

http://groups.google.com/group/jquery-dev/browse_thread/thread/73ca6be8071479fb