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.

转 Using $.ajaxPrefilter() To Configure AJAX Requests In jQuery 1.5的更多相关文章

  1. [React] Create a queue of Ajax requests with redux-observable and group the results.

    With redux-observable, we have the power of RxJS at our disposal - this means tasks that would other ...

  2. 三、jQuery--Ajax基础--Ajax全接触--jQuery中的AJAX

    用jQuery实现Ajax jQuery.ajax([settings]) type:类型,“POST”或“GET”,默认为“GET” url:发送请求的地址 data:是一个对象,连同请求发送到服务 ...

  3. Ajax:后台jquery实现ajax无刷新删除数据及demo

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAA8gAAAFSCAIAAAChUmFZAAAgAElEQVR4nO29z4scWZbn2/+Hb30zi8

  4. Allow Only Ajax Requests For An Action In ASP.NET Core

    ASP.NET Core offers attributes such as [HttpGet] and [HttpPost] that allow you to restrict the HTTP ...

  5. MVC Ajax Helper或jQuery异步方式加载部分视图

    Model: namespace MvcApplication1.Models { public class Team { public string Preletter { get; set; } ...

  6. MVC中使用Ajax提交数据 Jquery Ajax方法传值到action

    Jquery Ajax方法传值到action <script type="text/javascript"> $(document).ready(function(){ ...

  7. JavaScript封装Ajax(类JQuery中$.ajax()方法)

    ajax.js (function(exports, document, undefined){ "use strict"; function Ajax(){ if(!(this ...

  8. python_way day21 Django文件上传Form方式提交,原生Ajax提交字符处啊,Django文件上传之原生Ajax方式、jQuery Ajax方式、iframe方式,Django验证码,抽屉示例,

    python_way day21 1.Django文件上传至Form方式 2.原生Ajax文件上传提交表单 使用原生Ajax好处:不依赖jquery,在发送一个很小的文件或者字符串的时候就可以用原生A ...

  9. 最最基层的ajax交互代码jquery+java之间的json跨域传递以及java的json代码返回

    首先导入jar包 上面的jar包主要是用来将map或list数据转换成json字符串,传递到前台使用. 静态页面的代码:2.html <!DOCTYPE html> <html> ...

随机推荐

  1. Java基础——XML复习

    XML                 SGML : 标准通用置标语言    Standard Generailzed    Markup Language XML                   ...

  2. Question 20171117 Java中的编码问题?

    撰文缘由 前几天做一个邮件发送功能,一些常用信息配置在properties文件中,通过prop.getProperty(key)来获取配置的信息,结果配置文件中是用中文写的,邮件发送成功后,邮箱中的激 ...

  3. c#数据库连接池Hikari重构升级

    Hikari是我自定义的数据库连接池,前面已经提供了地址.因为c#的连接池按照规范的ADO.NET里面实现定义的.由数据库官方提供,但是实现方式就不知道了,反正没有看出来,估计一般是连接类实现的,但是 ...

  4. 【TOJ 4475】The Coolest Sub-matrix(对角线前缀和)

    描述 Given an N*N matrix, find the coolest square sub-matrix.We define the cool value of the square ma ...

  5. PyCharm入门第一步-——创建并运行第一个Python项目

    创建项目 点击Create New Project 创建项目 输入自己的项目名,点击Create创建 创建文件 右键项目名创建python文件 创建一个HelloPython文件 输入print(&q ...

  6. koa2 mongdb 做后端接口的小demo

    现在前端全栈里面有一种技术栈比较火 前端使用 vue 或者react 后端使用 koa2 mysql数据库 或者mongdb做数据储存 但是基本这样的全栈教程 都要收费 收费就收费吧 但是 有没有遇到 ...

  7. (转)程序员新人怎样在复杂代码中找 bug?

    我曾经做了两年大型软件的维护工作,那个项目有10多年了,大约3000万行以上的代码,参与过开发的有数千人,代码checkout出来有大约5个GB,而且bug特别多,open的有上千,即使最高优先级的s ...

  8. ccf201703-2 STLlist

    题目:http://118.190.20.162/view.page?gpid=T56 问题描述 体育老师小明要将自己班上的学生按顺序排队.他首先让学生按学号从小到大的顺序排成一排,学号小的排在前面, ...

  9. shell重温---基础篇(函数操作)

        linux shell 可以用户定义函数,然后在shell脚本中可以随便调用.shell中函数的定义格式如下: [ function ] funname [()] { action; [ret ...

  10. Java线程:概念与使用

    Java线程大总结 原文章地址:一篇很老的专栏,但是现在看起来也感觉深受启发,知识点很多,很多线程特点我没有看,尴尬.但是还是整理了一下排版,转载一下. 操作系统中线程和进程的概念 在现代操作系统中, ...