HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Wrapper for jquery ajax to ensure redirects occur on clientside

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
ajaxoccurensurewrapperjqueryforredirectsclientside

Problem

Recently I had the need to make some ajax calls within a MVC 3.0 with Razor and jQuery application. After a bit of trial and error and refactoring it was discovered that a number of different needs were required. These included:

  • The serverside code would be responsible for creating any redirect urls and we wanted to stick with the inherited controller RedirectToAction() method where possible in our controller actions.



  • We are using jQuery unobtrusive validation and some situations meant that new forms were rendered onto the view via ajax. That meant we needed to ensure those new form fields contained and validated when required.



  • We wanted the flexibility to return html (PartialViews), json or tell the client side it was a redirect and the URL to redirect to (initiated via 1 above).



  • Before page redirects at times we wanted to show div popups such as a Success confirmation. Hence although we were using RedirectToAction() in the backend we wanted the javascript to actually perform the redirect.



With these in place we did a bit of serverside code that ensured we hijacked the RedirectToAction() result and returned a json object instead. I'm fairly happy that code works. What I'm not sure about is the javascript code that I use to perform the result.

Some notes:

  • The $.validator.unobtrusive.parseDynamicContent method will ensure the new html elements are rebound for validation. I did not write that code so have not added it for review.



  • For options 1, 2 and 3 above we didn't want the person writing the ajax to always have to worry about these so esentially wanted them to just be handled so to speak.



I'm definitely pretty raw in the Javascript department so any comments or improvements on the code below would be greatly appreciated.

```
$.ajaxWithRedirect = function (options) {

// If dataType wasn't specified in the options, default to 'html'
var dataType = (options.dataType !== undefined) ? options.dataType : 'html';

// jQuery

Solution

It seems to me that you're duplicating some of what $.ajax() will do for you (such as calling beforeSend) and you're also leaving the door open for using the deferred promise methods (then, done, fail, etc.) that jQuery's XHR object provides.

Here's a version that focusses solely on the redirection (i.e. it doesn't worry about the data type, the validation stuff, or doing its own error handling). It's just a transparent layer on top of the normal $.ajax() function.

The code's below, and here's a demo

$.ajaxWithRedirect = function (options) {
  "use strict";

  var deferred = $.Deferred(),
      successHandler,
      xhr;

  // force no-cache
  options.cache = false;

  // get a copy of the success handler...
  successHandler = options.success;

  // ... and replace it with this one
  options.success = function (data) {
    var contentType = xhr.getResponseHeader("Content-Type"),
        performRedirect = true,
        redirectUrl = null,
        args = [].slice.call(arguments, 0),
        json;

    // If response isn't there or isn't json,
    // skip all the redirect logic
    if( data && (/json/i).test(contentType) ) {
      // If json was requested, and json received, jQuery will
      // have parsed it already. Otherwise, we'll have to do it
      if( options.dataType === 'json' ) {
        redirectUrl = data.RedirectUrl;
      } else {
        try {
          json = $.parseJSON(data);
          redirectUrl = json.RedirectUrl;
        } catch(e) {
          // no-op
        }
      }

      // check the redirect url
      if( redirectUrl && typeof redirectUrl === 'string') {
        // Is there a beforeRedirect handler?
        if( typeof options.beforeRedirect === 'function' ) {
          // pass all the arguments to the beforeRedirect handler
          performRedirect = options.beforeRedirect.apply(null, args);
        }

        // unless strictly false, go ahead with the redirect
        if( performRedirect !== false ) {
          location.replace(redirectUrl);
          // and stop here. No success and/or deferred handlers
          // will be called since we're redirecting anyway
          return;
        }
      }
    }

    // no redirect; forward everything to the success handler(s)
    if( typeof successHandler === 'function' ) {
      successHandler.apply(null, args);
      deferred.resolve.apply(null, args);
    }
  };

  // Make the request
  xhr = $.ajax(options);

  // Forward the deferred promise method(s)
  xhr.fail(deferred.reject);
  xhr.progress(deferred.notify);

  // Replace the ones already on the xhr obj
  deferred.promise(xhr);

  return xhr;
};


Point is that you should be able use $.ajaxWithRedirect exactly like you'd use vanilla $.ajax, including all the shiny deferred promise stuff. The only thing it does different from the normal version is set options.cache = false, and that it has a beforeRedirect callback.

Code Snippets

$.ajaxWithRedirect = function (options) {
  "use strict";

  var deferred = $.Deferred(),
      successHandler,
      xhr;

  // force no-cache
  options.cache = false;

  // get a copy of the success handler...
  successHandler = options.success;

  // ... and replace it with this one
  options.success = function (data) {
    var contentType = xhr.getResponseHeader("Content-Type"),
        performRedirect = true,
        redirectUrl = null,
        args = [].slice.call(arguments, 0),
        json;

    // If response isn't there or isn't json,
    // skip all the redirect logic
    if( data && (/json/i).test(contentType) ) {
      // If json was requested, and json received, jQuery will
      // have parsed it already. Otherwise, we'll have to do it
      if( options.dataType === 'json' ) {
        redirectUrl = data.RedirectUrl;
      } else {
        try {
          json = $.parseJSON(data);
          redirectUrl = json.RedirectUrl;
        } catch(e) {
          // no-op
        }
      }

      // check the redirect url
      if( redirectUrl && typeof redirectUrl === 'string') {
        // Is there a beforeRedirect handler?
        if( typeof options.beforeRedirect === 'function' ) {
          // pass all the arguments to the beforeRedirect handler
          performRedirect = options.beforeRedirect.apply(null, args);
        }

        // unless strictly false, go ahead with the redirect
        if( performRedirect !== false ) {
          location.replace(redirectUrl);
          // and stop here. No success and/or deferred handlers
          // will be called since we're redirecting anyway
          return;
        }
      }
    }

    // no redirect; forward everything to the success handler(s)
    if( typeof successHandler === 'function' ) {
      successHandler.apply(null, args);
      deferred.resolve.apply(null, args);
    }
  };

  // Make the request
  xhr = $.ajax(options);

  // Forward the deferred promise method(s)
  xhr.fail(deferred.reject);
  xhr.progress(deferred.notify);

  // Replace the ones already on the xhr obj
  deferred.promise(xhr);

  return xhr;
};

Context

StackExchange Code Review Q#17830, answer score: 4

Revisions (0)

No revisions yet.