zoukankan      html  css  js  c++  java
  • 转 Using $.ajaxPrefilter() To Configure AJAX Requests In jQuery 1.5

    Using $.ajaxPrefilter() To Configure AJAX Requests In jQuery 1.5

    Posted February 18, 2011 at 6:29 PM

    Tags: Javascript / DHTML

    Last week, I started to explore Deferred objects and the AJAX updates made to jQuery 1.5 byJulian Aubourg. To kick off the exploration, I created a function that would essentially proxy the XHR request object, normalizing the core response in order to account for web services that use meaningful HTTP status codes in order to define request errors. In the comments to that post, Julian Aubourg himself pointed out that I could use the new $.ajaxPrefilter() method as a means to implement the same functionality in a much more flexible way. To help wrap my head around his comments, I decided to refactor my previous post using a combination of $.ajaxSetup() and $.ajaxPrefilter().

             
         
         

    As a quick recap, jQuery only parses "success" responses returned from the server. That means that requests with 40x and 50x HTTP status codes do not get parsed. The problem with this is that web services often return status codes like 400 and 401 in order to provide a better context for the response data. In order to make sure that jQuery parses the JSON data associated with these non-200 responses, we need to proxy the AJAX request and augment the routing logic.

    In my previous post, I created this AJAX proxy by manually wrapping the core jqXHR object as part of my AJAX request. As Julian pointed out, however, it would be much better if I could leave this kind of object decoration up to a configuration option. By using his approach, not only would I factor out the burden of creating the proxy object, I would also allow the request normalization to be done either on a global or one-off basis.

    To see what I'm talking about, take a look at this demo code. As you read through it, notice that I am using $.ajaxSetup() to apply the normalization and then $.ajaxPrefilter() to implement it.

    NOTE: I am not going to reproduce the ColdFusion API code since it is not really relevant to this exploration. If you like, you can alway refer to my previous post.

     Launch code in new window » Download code as text file »

    • <!DOCTYPE html>
    • <html>
    • <head>
    • <title>Using $.ajaxPrefilter() With AJAX In jQuery 1.5</title>
    • <script type="text/javascript" src="../jquery-1.5.js"></script>
    • </head>
    • <body>
    •  
    • <h1>
    • Using $.ajaxPrefilter() With AJAX In jQuery 1.5
    • </h1>
    •  
    • <form>
    •  
    • <h2>
    • Enter Data
    • </h2>
    •  
    • <p class="message" style="display: none ;">
    • <!--
    • This is where the confirmation message will go
    • on the AJAX request completion.
    • -->
    • </p>
    •  
    • <p>
    • Username:
    • <input type="text" name="username" size="20" />
    • </p>
    •  
    • <p>
    • Name:
    • <input type="text" name="name" size="20" />
    • </p>
    •  
    • <p>
    • Age:
    • <input type="text" name="age" size="5" />
    • </p>
    •  
    • <p>
    • <input type="submit" value="Save Contact" />
    • </p>
    •  
    • </form>
    •  
    •  
    • <!-- --------------------------------------------------- -->
    • <!-- --------------------------------------------------- -->
    •  
    •  
    • <script type="text/javascript">
    •  
    • // Store DOM references.
    • var form = $( "form" );
    • var message = $( "p.message" );
    • var username = form.find( "input[ name = 'username' ]" );
    • var contactName = form.find( "input[ name = 'name' ]" );
    • var contactAge = form.find( "input[ name = 'age' ]" );
    •  
    •  
    • // This page is part of an application that calls a web
    • // service that returns response values with the most
    • // appropriate status codes. We want those status codes
    • // to trigger done/fail callbacks with parsed values.
    • $.ajaxSetup(
    • {
    • normalizeForStatusCodes: true
    • }
    • );
    •  
    •  
    • // Bind to the form submission error to handle it via AJAX
    • // rather than through the standard HTTP request.
    • form.submit(
    • function( event ){
    •  
    • // Prevent the default browser behavior.
    • event.preventDefault();
    •  
    • // Try to save the contact to the server. The
    • // saveContact() method returnes a promise object
    • // which will come back with a result eventually.
    • // Depending on how it resolves, either the done()
    • // or fail() event handlers will be invoked.
    • //
    • // NOTE: This return object can be chained; but for
    • // clarity reasons, I am leaving these as one-offs.
    • var saveAction = saveContact(
    • username.val(),
    • contactName.val(),
    • contactAge.val()
    • );
    •  
    • // Hook into the "success" outcome.
    • saveAction.done(
    • function( response ){
    •  
    • // Output success message.
    • message.text(
    • "Contact " + response.data + " saved!"
    • );
    •  
    • // Show the message.
    • message.show();
    •  
    • }
    • );
    •  
    • // Hook into the "fail" outcome.
    • saveAction.fail(
    • function( response ){
    •  
    • // Output fail message.
    • message.html(
    • "Please review the following<br />-- " +
    • response.errors.join( "<br />-- " )
    • );
    •  
    • // Show the message.
    • message.show();
    •  
    • }
    • );
    •  
    • }
    • );
    •  
    •  
    • // I save the contact data.
    • function saveContact( username, name, age ){
    • // Initiate the AJAX request. This will return an
    • // AJAX promise object that maps (mostly) to the
    • // standard done/fail promise interface.
    • var request = $.ajax({
    • type: "post",
    • url: "./api.cfm",
    • data: {
    • username: username,
    • name: name,
    • age: age
    • }
    • });
    •  
    • // Return the jqXHR promise object.
    • return( request );
    • }
    •  
    •  
    • // -------------------------------------------------- //
    • // -------------------------------------------------- //
    • // -------------------------------------------------- //
    • // -------------------------------------------------- //
    • // -------------------------------------------------- //
    • // -------------------------------------------------- //
    •  
    •  
    • // Here, we are providing a way to normalize AJAX responses
    • // to web services make proper use of status codes when
    • // returning request values. This will look for a
    • // "normalizeForStatusCodes" option before altering the
    • // jqXHR object.
    • $.ajaxPrefilter(
    •  
    • function( options, localOptions, jqXHR ){
    •  
    • // Check to see if this request is going to require
    • // a normalization based on status codes.
    • if (options.normalizeForStatusCodes){
    •  
    • // The user wants the response status codes to
    • // be handled as part of the routing; augment the
    • // jqXHR object to parse "fail" responses.
    • normalizeAJAXRequestForStatusCodes( jqXHR );
    •  
    • }
    •  
    • }
    •  
    • );
    •  
    •  
    • // I take the AJAX request and return a new deferred object
    • // that is able to normalize the response from the server so
    • // that all of the done/fail handlers can treat the incoming
    • // data in a standardized, unifor manner.
    • function normalizeAJAXRequestForStatusCodes( jqXHR ){
    • // Create an object to hold our normalized deferred.
    • // Since AJAX errors don't get parsed, we need to
    • // create a proxy that will handle that for us.
    • var normalizedRequest = $.Deferred();
    •  
    • // Bind the done/fail aspects of the original AJAX
    • // request. We can use these hooks to resolve our
    • // normalized AJAX request.
    • jqXHR.then(
    •  
    • // SUCCESS hook. ------ //
    • // Simply pass this onto the normalized
    • // response object (with a success-based resolve).
    • normalizedRequest.resolve,
    •  
    • // FAIL hook. -------- //
    • function( jqXHR ){
    •  
    • // Check to see what the status code of the
    • // response was. A 500 response will represent
    • // an unexpected error. Anything else is simply
    • // a non-20x error that needs to be manually
    • // parsed.
    • if (jqXHR.status == 500){
    •  
    • // Normalize the fail() response.
    • normalizedRequest.rejectWith(
    • this,
    • [
    • {
    • success: false,
    • data: "",
    • errors: [ "Unexpected error." ],
    • statusCode: jqXHR.statusCode()
    • },
    • "error",
    • jqXHR
    • ]
    • );
    •  
    • } else {
    •  
    • // Normalize the non-500 "failures." These
    • // are actually valid responses that require
    • // actions on the part of the user.
    • normalizedRequest.rejectWith(
    • this,
    • [
    • $.parseJSON( jqXHR.responseText ),
    • "success",
    • jqXHR
    • ]
    • );
    •  
    • }
    •  
    • }
    •  
    • );
    •  
    •  
    • // We can't actually return anything meaningful from this
    • // function; but, we can augment the incoming jqXHR
    • // object. Right now, the incoming jqXHR promise methods
    • // reference their original settings; however, we can
    • // copy the locally-created deferred object methods into
    • // the existing jqXHR. This will keep the jqXHR objec the
    • // same reference -- but, it will essentially change all
    • // the meaningful bindingds.
    • jqXHR = normalizedRequest.promise( jqXHR );
    •  
    • // At this point, the promise-based methods of the jqXHR
    • // are actually the locally-declared ones. Now, we just
    • // have to point the sucecss and error methods (AJAX
    • // related) to the done and fail methods (promise
    • // related).
    • jqXHR.success = jqXHR.done;
    • jqXHR.error = jqXHR.fail;
    •  
    • // NOTE: No need to return anything since the jqXHR
    • // object is being passed by reference.
    • }
    •  
    • </script>
    •  
    • </body>
    • </html>

    At the top of the code, I am using $.ajaxSetup() to indicate that all AJAX requests will be made to a web service that uses targeted HTTP status codes in order to help define its response.

     Launch code in new window » Download code as text file »

    • $.ajaxSetup(
    • {
    • normalizeForStatusCodes: true
    • }
    • );

    Really, all this is doing is helping to define a base hash of AJAX configuration options that will be used by AJAX requests on the current page. This can be overridden by each AJAX request - $.ajax(); but for our purposes, we'll stick with the global configuration.

    And, once we have the global configuration in place, we use the $.ajaxPrefilter() method to augment the jQuery XHR object (jqXHR) based on the configuration options.

     Launch code in new window » Download code as text file »

    • $.ajaxPrefilter(
    • function( options, localOptions, jqXHR ){
    •  
    • if (options.normalizeForStatusCodes){
    •  
    • normalizeAJAXRequestForStatusCodes( jqXHR );
    •  
    • }
    •  
    • }
    • );

    The function reference passed to the $.ajaxPrefilter() method will be executed for every single AJAX request. In this case, we are looking at the merged options hash (NOTE: localOptions are the options defined directly by the $.ajax() method) and, if the "normalizeForStatusCodes" option is true, we are augmenting the jqXHR object.

    The augmentation of the jqXHR object is being encapsulated within the function, normalizeAJAXRequestForStatusCodes(). I am doing this mostly because it helps me think about the problem in isolated, compartmentalized steps. Of course, we aren't just moving code around - we're changing the way it works; and, when we use $.ajaxPrefilter(), we can't return a proxy object in the same way that we were doing before. Rather, we need to directly manipulate the outgoing jqXHR object.

    As per Julian's lead, I am doing this by calling the promise() method on my deferred-bridge and passing-in the jqXHR object:

     Launch code in new window » Download code as text file »

    • jqXHR = normalizedRequest.promise( jqXHR );
    • jqXHR.success = jqXHR.done;
    • jqXHR.error = jqXHR.fail;

    The name, "promise," is kind of misleading here - we're not really creating a new object; when you pass an object (as an argument) into the promise() method, what you're actually doing is copying the method references from one object to the other. So, in this case, we are copying all of the promise-based methods - then, done, fail, isResolved, isRejected, promise - from the normalizedRequest Deferred object into the jqXHR object.

    This is actually super interesting! This works because the object reference never changes - only its properties do. And, since the properties consist of methods that are lexically bound, you're essentially creating an in-place proxy object, leaving the same outer shell intact. There's something deeply satisfying about that.

    I'm still wrapping my head around all of the new jQuery 1.5 functionality; but, a huge thanks to Julian for point this stuff out to me. There are, of course, other ways to configure outgoing AJAX requests in jQuery such as the beforeSend callback; but, this combination of $.ajaxSetup() and $.ajaxPrefilter() just feels very clean.

  • 相关阅读:
    嵌入式
    c语言
    c++的内存分配
    免费的Skype没有打败固定电话,而付费的移动电话却战胜了固定电话,免费看来并不是关键。在蓝光还没有彻底取代DVD的时候,在线视频出现了,同时颠覆了DVD和蓝光光盘,无论你的光盘容量变得如何大,都不能代表未来,只有在线才是未来。
    直到有了3G,用户才第一次体验到了永远在线的感觉,如果用手机上网也许比找个电源插座还简单,这就是在线的普及趋势。而3G只是个开始,4G、5G会让在线的速度更快更流畅。
    50多万年前的关键字是光明与黑暗,50多年前的关键字是数位和模拟,而今天的关键字是在线与离线。
    “大数据”这个名字叫错了,今天数据的意义并不在于有多“大”,真正有意思的是数据变得在线了。
    云计算是关于信任的生意,有人说他要做三年赚200亿美元的互联网项目,但他要依靠别人的云计算服务,没有这个勇气,是不可能创新的。
    做APP是别人的花园里弄盆栽,因为苹果和安卓已经圈了一个花园,你种点花草是没有问题的,但是想做点有生命力的东西,还是有挑战性的。
    私有云根本就称不上是云,云计算的本质是服务,如果不能够将计算资源规模化、大范围地进行共享,就根本算不上是云计算,传统IT巨头所谓的“私有云”就换汤不换药的升级版IT解决方案。
  • 原文地址:https://www.cnblogs.com/xuxiang/p/3425691.html
Copyright © 2011-2022 走看看