Ajax -- HTTP请求与响应
Ajax是Adaptive Path的Jesse James Garrett(同时也是《用户体验要素》的作者)提出的一个名词。他在解释XMLHttpRequest对象宋进行的异步的客户端到服务器端通信时,使用了这一名词。作为异步JavaScript与XML(Asynchronous JavaScript and XML)的缩写,它只是创建动态Web应用程序必备技术的一个统称。基本的Ajax内容从具体的请求到最后的数据操作,它包括:□ 分析不同类型的HTTP请求,决定以何种方式将数据对象发送到服务器最合适
□ 观察整个HTTP响应过程,尝试处理所有可能发生的错误,包括服务器的超时
□ 读取、遍历并操作服务器响应的结果数据
1. HTTP请求
Ajax中最重要也是最固定的部分是HTTP请求。归根结底,Ajax的目的是异步地向服务器发送数据并接收数据。至于数据是什么格式,取决于具体的文档。
建立连接
这部分主要设计如何格式化数据,以便使用不同的HTTP请求传输到服务器。还要解决如何与服务器建立基本的连接,以及在跨浏览器环境中处理这些的必要细节。
根据用户浏览器的不同,XMLHttpRequest对象的数据通信通常有两种方法实现:
(1)IE的ActiveXObject。IE7对XMLHttpRequest对象已经有了直接的支持
(2)其它所有的现代浏览器都直接支持XMLHttpRequest对象
一种向服务器发送HTTP GET请求的跨浏览器方法:
[code lang="js"]
// 如果是IE
if ( typeof XMLHttpRequest == "undefined" )
XMLHttpRequest = function(){
// IE使用 ActiveXObject
return new ActiveXObject(
// 区分IE5和IE6
navigator.userAgent.indexOf("MSIE 5") >= 0 ?
"Microsoft.XMLHTTP" : "Msxml2.XMLHTTP"
);
};
// 创建新的请求对象
var xml = new XMLHttpRequest();
// 初始化请求
xml.open("GET", "/some/url.cgi", true);
// 与服务器连接并发送数据
xml.send();
[/code]
Ajax方法中最重要的特性是允许数据在客户端和服务器端之间传输,有鉴于此,接下来讨论如何包装数据来发送到服务器。
数据的串行化
要发送一系列数据到服务器上,首先就是要整理它的格式,使服务器便于读取,这一过程称为“串行化(serialization)”。串行化有两种不同的情况,但都能满足多种不同的传输要求。
(1)传输常规的JavaScript对象,其中包括键/值的对
(2)一系列表单字段。这种情况元素是按顺序排列的,而第一种情况可以任意次序。
将原生JavaScript对象转化为串行形式的例子:
[code lang="js"]
// 键/值对
{
name: "Mark",
last: "Zuckerberg",
city: "Cambridge",
zip: 02140
}
// 串行形式
name=Mark&last=Zuckerberg&city=Cambridge&zip=02140
// 另一组数据
[
{ name: "name", value: "Mark" },
{ name: "last", value: "Zuckerberg" },
{ name: "lang", value: "JavaScript" },
{ name: "lang", value: "Perl" },
{ name: "lang", value: "Java" }
]
// 串行形式
name=Mark&last=Zuckerberg&lang=JavaScript&lang=Perl&lang=Java
// 使用id方法生成的键/值对
[
id( "name" ),
id( "last" ),
id( "username" ),
id( "password" )
]
// 将其串行化为字符串
name=Mark&last=Zuckerberg&username=jeresig&password=test
[/code]
现在建立一个将上述数据串行化的标准方法:
[code lang="js"]
// 串行化数据。支持两种不同的对象
// - 表单字段的数组
// - 键值对的哈希表
// 返回串行化的字符串
function serialize(a) {
// 结果数组
var s = [];
// 如果参数是数组,则假定他们是表单字段
if ( a.constructor == Array ) {
// 串行化表单字段
for ( var i = 0; i < a.length; i++ )
s.push( a[i].name + "=" + encodeURIComponent( a[i].value ) );
// 否则,假定它是键值对
} else {
// 串行化键值对
for ( var j in a )
s.push( j + "=" + encodeURIComponent( a[j] ) );
}
// 返回串行化后的结果
return s.join("&");
}
[/code]
现在数据都转成了串行形式的字符串,接下来使用GET或POST请求发送数据到服务器上
发送GET请求
一个跨浏览器的向服务器发送HTTP GET请求的方法
[code lang="js"]
// 建立异步请求
var xml = new XMLHttpRequest();
// 初始化异步get请求
xml.open("GET", "/some/url.cgi?" + serialize( data ), true);
// 与服务器建立连接
xml.send();
[/code]
串行数据附在服务器url后面,用?字符分隔。
发送POST请求
使用XMLHttpRequest发送HTTP请求的另一种方式是POST,这种方式与GET方式完全不同。POST支持发送任意格式、任意长度的数据,而不仅局限与串行化字符串。用于发送串行数据的MIME类型(content type)通常是application/x-www-form-urlencoded。同样还可以以text/xml或application/xml的形式直接发送XML,甚至以application/json的形式发送JavaScript对象。
一种跨浏览器的向服务器发送HTTP POST请求的方法
[code lang="js"]
// 建立异步请求
var xml = new XMLHttpRequest();
// 初始化异步post请求
xml.open("POST", "/some/url.cgi", true);
// 设置 content-type,通知服务器如何解析发送的数据
xml.setRequestHeader(
"Content-Type", "application/x-www-form-urlencoded");
// 保证浏览器发送的串行数据长度正确
if ( xml.overrideMimeType )
xml.setRequestHeader("Connection", "close");
// 与服务器建立连接,并发送串行化数据
xml.send( serialize( data ) );
[/code]
将XML数据发送到服务器的例子:
[code lang="js"]
// 建立异步请求
var xml = new XMLHttpRequest();
// 初始化异步post请求
xml.open("POST", "/some/url.cgi", true);
// 设置 content-type,通知服务器如何解析发送的数据
xml.setRequestHeader( "Content-Type", "text/xml");
// 保证浏览器发送的串行数据长度正确
if ( xml.overrideMimeType )
xml.setRequestHeader("Connection", "close");
// 与服务器建立连接,并发送串行化数据
xml.send( "" );
[/code]
2. HTTP响应
使用XMLHttpRequest对象的优越之处在于,它能够从服务器读取不同格式的数据,不仅仅是XML,还可以是HTML或者JSON等。首先用一个简化的例子来演示处理服务器数据的步骤:
[code lang="js"]
// 建立异步请求
var xml = new XMLHttpRequest();
// 初始化异步post请求
xml.open("GET", "/some/url.cgi", true);
// 在文档的状态更新时调用
xml.onreadystatechange = function(){
// 数据完整加载
if ( xml.readyState == 4 ) {
// xml.responseXML 包含XML文档(如果返回的是XML)
// xml.responseText 包含文本(如果返回的不是XML)
// 为避免内存泄漏,清理文档
xml = null;
}
};
// 与服务器建立连接
xml.send();
[/code]
在这里,responseXML和responseText属性分别包含对应格式的数据。在对响应数据进行实际的处理、遍历和操作之前,首先创建一个更健壮的onreadystatechange函数,用来处理服务器错误和超时。
处理错误
处理错误情况需要检查以下状况:
□ 成功响应代码:通过HTTP规范里定义的响应状态码来检查错误。状态码在200到300之间的属于成功的请求
□ 未修改响应:服务器返回"Not Modified"标记,也就是状态码304。说明服务器返回的数据和浏览器的缓存内容一致,并未修改过
□ 本地文件:如果在本机上直接执行Ajax代码,而不通过Web服务器,就算请求成功了,也不会得到任何状态码。这意味着,在执行本地文件且得不到状态码时,应该把这种情况算作成功的响应
□ Safari与未修改状态:如果文档自上次请求未曾修改过,Safari返回的状态码会是"undefined"
用于检查服务器HTTP响应的成功状态函数:
[code lang="js"]
// 检查 XMLHttpRequest 对象的状态码是否为 'Success'
// 此函数需要一个 XMLHttpRequest 对象作为参数
function httpSuccess(r) {
try {
// 如果得不到服务器状态,且正在请求本地文件,则返回 true
return !r.status && location.protocol == "file:" ||
// 200 至 300 之间的状态码,返回 true
( r.status >= 200 && r.status < 300 ) ||
// 文档为修改也返回 true
r.status == 304 ||
// Safari在文档修改时返回空状态,返回true
navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined";
} catch(e){}
// 否则,返回false
return false;
}
[/code]
检查超时
在XMLHttpRequest的默认实现中缺少判断请求超时的功能。下面是检查请求超时的一个例子:
[code lang="js"]
// 建立异步请求
var xml = new XMLHttpRequest();
// 初始化异步post请求
xml.open("GET", "/some/url.cgi", true);
// 超时时长为5秒
var timeoutLength = 5000;
// 跟踪请求是否完成的变量
var requestDone = false;
// 初始化一个5秒后执行的回调函数,用于取消请求
setTimeout(function(){
requestDone = true;
}, timeoutLength);
// 监听文档状态
xml.onreadystatechange = function(){
// 等待数据完全加载,并保证请求并未超时
if ( xml.readyState == 4 && !requestDone ) {
// xml.responseXML 包含XML文档(如果返回的是XML)
// xml.responseText 包含文本(如果返回的不是XML
// 为避免内存泄漏,清理文档
xml = null;
}
};
// 与服务器建立连接
xml.send();
[/code]
3. 处理响应数据
在服务器上可能返回3种数据格式,并用客户端来读取并操作它们:
□ XML:所有浏览器都提供了原生的XML文档处理支持,自动将它们转换为可用的DOM文档
□ HTML:和XML文档的区别在于,它通常以纯文本字符串的形式存放在一个HTML片段中
□ JSON:JavaScript Object Notation格式
这3总格式都各有其适合的场合,比如有时返回HTML要比返回XML更有意义。获取HTTP响应数据的重点是XMLHttpRequest对象的两个属性:
□ responseXML:如果服务器返回的是XML文档,这个属性包含预处理后DOM文档的引用。
□ responseText:包含从服务器返回的原始文本数据的引用。
从HTTP服务器响应中解析正确数据的一个函数:
[code lang="js"]
// 从HTTP响应中解析数据的函数
// 有两个参数:一个XMLHttpRequest对象,和一个可选参数(从服务器返回的数据类型)
// type的值包括:xml,script,text或html,默认是""
function httpData(r, type) {
// 获取content-type头部信息
var ct = r.getResponseHeader("content-type");
// 若没有提供默认的类型,判断服务器返回的是否是XML类型
var data = !type && ct && ct.indexOf("xml") >= 0;
// 如果为真则获得XML文档对象,否则返回文本内容
data = type == "xml" || data ? r.responseXML : r.responseText;
// 如果指定类型为script,则以JavaScript形式返回文本
if ( type == "script" )
eval.call( window, data );
// 返回数据(或为XML文档,或为文本字符串)
return data;
}
[/code]
4. 能够执行必要的Ajax相关任务的一个完整函数
[code lang="js"]
// 执行Ajax请求的通用函数
// 包含一系列选项的参数
function ajax( options ) {
// 如果没有提供选项值,则采用默认值
options = {
// HTTP请求的类型
type: options.type || "POST",
// 请求的url
url: options.url || "",
// 请求的超时时间
timeout: options.timeout || 5000,
// 请求完成、失败或成功时执行的函数
onComplete: options.onComplete || function(){},
onError: options.onError || function(){},
onSuccess: options.onSuccess || function(){},
// 服务器返回的数据类型
data: options.data || ""
};
// 建立异步请求
var xml = new XMLHttpRequest();
// 初始化异步post请求
xml.open("GET", "/some/url.cgi", true);
// 超时时长为5秒
var timeoutLength = 5000;
// 跟踪请求是否完成的变量
var requestDone = false;
// 初始化一个5秒后执行的回调函数,用于取消请
setTimeout(function(){
requestDone = true;
}, timeoutLength);
// 监听文档状态
xml.onreadystatechange = function(){
// 等待数据完全加载,并保证请求并未超时
if ( xml.readyState == 4 && !requestDone ) {
// 检查请求是否成功
if ( httpSuccess( xml ) ) {
// 以服务器返回的数据作为参数调用onSuccess函数
options.onSuccess( httpData( xml, options.type ) );
// 否则就发生了错误,执行onError函数
} else {
options.onError();
}
// 调用onComplete回调函数
options.onComplete();
// 为避免内存泄漏,清理文档
xml = null;
}
};
// 与服务器建立连接
xml.send();
// 判断HTTP响应是否成功
function httpSuccess(r) {
try {
// 如果得不到服务器状态,且正在请求本地文件,则返回 true
return !r.status && location.protocol == "file:" ||
// 200 至 300 之间的状态码,返回 true
( r.status >= 200 && r.status < 300 ) ||
// 文档为修改也返回 true
r.status == 304 ||
// Safari在文档修改时返回空状态,返回true
navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined";
} catch(e){}
// 否则,返回false
return false;
}
// 从HTTP相应中解析数据
function httpData(r,type) {
// 获取content-type头部信息
var ct = r.getResponseHeader("content-type");
// 若没有提供默认的类型,判断服务器返回的是否是XML类型
var data = !type && ct && ct.indexOf("xml") >= 0;
// 如果为真则获得XML文档对象,否则返回文本内容
data = type == "xml" || data ? r.responseXML : r.responseText;
// 如果指定类型为script,则以JavaScript形式返回文本
if ( type == "script" )
eval.call( window, data );
// 返回数据(或为XML文档,或为文本字符串)
return data;
}
}
[/code]
查看完整版本: Ajax -- HTTP请求与响应
Tags: