Ajax — HTTP请求与响应

前端技术 everyinch 9183℃ 0评论

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请求的跨浏览器方法:

// 如果是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();

Ajax方法中最重要的特性是允许数据在客户端和服务器端之间传输,有鉴于此,接下来讨论如何包装数据来发送到服务器。
数据的串行化
要发送一系列数据到服务器上,首先就是要整理它的格式,使服务器便于读取,这一过程称为“串行化(serialization)”。串行化有两种不同的情况,但都能满足多种不同的传输要求。
(1)传输常规的JavaScript对象,其中包括键/值的对
(2)一系列表单字段。这种情况元素是按顺序排列的,而第一种情况可以任意次序。
将原生JavaScript对象转化为串行形式的例子:

// 键/值对
{
    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

现在建立一个将上述数据串行化的标准方法:

// 串行化数据。支持两种不同的对象
//  - 表单字段的数组
//  - 键值对的哈希表
// 返回串行化的字符串
function serialize(a) {
    // 结果数组
    var s = [];
		
    // 如果参数是数组,则假定他们是表单字段
    if ( a.constructor == Array ) {

        // 串行化表单字段
        for ( var i = 0; i < a.length; i++ )
            s.push( a&#91;i&#93;.name + "=" + encodeURIComponent( a&#91;i&#93;.value ) );
			
    // 否则,假定它是键值对
    } else {

        // 串行化键值对
        for ( var j in a )
            s.push( j + "=" + encodeURIComponent( a&#91;j&#93; ) );

    }
		
    // 返回串行化后的结果
    return s.join("&");
}
&#91;/code&#93;
现在数据都转成了串行形式的字符串,接下来使用GET或POST请求发送数据到服务器上
<strong>发送GET请求</strong>
一个跨浏览器的向服务器发送HTTP GET请求的方法

// 建立异步请求
var xml = new XMLHttpRequest();

// 初始化异步get请求
xml.open("GET", "/some/url.cgi?" + serialize( data ), true);

// 与服务器建立连接
xml.send();

串行数据附在服务器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请求的方法

// 建立异步请求
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 ) );

将XML数据发送到服务器的例子:

// 建立异步请求
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( "<items><item id='one'/><item id='two'/></items>" );

2. HTTP响应
使用XMLHttpRequest对象的优越之处在于,它能够从服务器读取不同格式的数据,不仅仅是XML,还可以是HTML或者JSON等。首先用一个简化的例子来演示处理服务器数据的步骤:

// 建立异步请求
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();

在这里,responseXML和responseText属性分别包含对应格式的数据。在对响应数据进行实际的处理、遍历和操作之前,首先创建一个更健壮的onreadystatechange函数,用来处理服务器错误和超时。
处理错误
处理错误情况需要检查以下状况:
□ 成功响应代码:通过HTTP规范里定义的响应状态码来检查错误。状态码在200到300之间的属于成功的请求
□ 未修改响应:服务器返回"Not Modified"标记,也就是状态码304。说明服务器返回的数据和浏览器的缓存内容一致,并未修改过
□ 本地文件:如果在本机上直接执行Ajax代码,而不通过Web服务器,就算请求成功了,也不会得到任何状态码。这意味着,在执行本地文件且得不到状态码时,应该把这种情况算作成功的响应
□ Safari与未修改状态:如果文档自上次请求未曾修改过,Safari返回的状态码会是"undefined"
用于检查服务器HTTP响应的成功状态函数:

// 检查 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;
}

检查超时
在XMLHttpRequest的默认实现中缺少判断请求超时的功能。下面是检查请求超时的一个例子:

// 建立异步请求
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();

3. 处理响应数据
在服务器上可能返回3种数据格式,并用客户端来读取并操作它们:
□ XML:所有浏览器都提供了原生的XML文档处理支持,自动将它们转换为可用的DOM文档
□ HTML:和XML文档的区别在于,它通常以纯文本字符串的形式存放在一个HTML片段中
□ JSON:JavaScript Object Notation格式
这3总格式都各有其适合的场合,比如有时返回HTML要比返回XML更有意义。获取HTTP响应数据的重点是XMLHttpRequest对象的两个属性:
□ responseXML:如果服务器返回的是XML文档,这个属性包含预处理后DOM文档的引用。
□ responseText:包含从服务器返回的原始文本数据的引用。
从HTTP服务器响应中解析正确数据的一个函数:

// 从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;
}

4. 能够执行必要的Ajax相关任务的一个完整函数

// 执行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;
    }
}
分享&收藏

转载请注明:陈童的博客 » Ajax — HTTP请求与响应

喜欢 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
'; } if( dopt('d_footcode_b') ) echo dopt('d_footcode'); ?>