原生Ajax讲解

典型的http通信:浏览器向服务器发出请求,服务器向客户端返回响应,浏览器重新加载页面,这种不连续的页面加载方式导致用户的体验变得杂乱,缺乏连贯性。

如:

在一般的web应用程序中,用户填写表单字段然后单击submit按钮,然后整个表单发送到服务器,服务器将它转发给处理该表单的脚本(PHP或JAVA),脚本执行完成后再发送回全新的页面。该页面返回的可能是带有已经填充某些数据的新表单的html,也可能返回的是一个确认页面。在服务器上的脚本处理该表单时和返回新页面的这段时间里,用户必须等待且屏幕是一片空白,等到服务器返回数据后再重新绘制出页面。

ajax,用户在填写表单时,数据就会发送给一些javascript代码而不是直接发送给服务器。javascript代码捕获表单数据并向服务器发送请求。此时,用户屏幕上的表单不会闪烁,消失或延迟。用户不用等待服务器的响应就可以继续输入数据,滚动屏幕和继续使用页面。然后,服务器将数据返回给javascript代码(仍然在web表单中),javascript代码决定如何处理这些数据。它可以迅速更新表单数据,让人感觉应用程序是立即完成的,表单没有提交或刷新而用户就得到了新数据。javascript甚至可以对收到的数据执行一些操作然后再发送另一个请求,完全不需要用户干预。这就是ajax的强大之处,它可以根据需要自行与服务器进行交互,用户甚至不知道幕后所发生的一切。



Ajax允许客户端javascript向服务器请求和接收数据,而无须刷新页面。这种技术允许开发人员创建不中断的应用程序,用新数据重载页面的某些部分。

Ajax表示使用javascript收发来自web服务器的数据,且无须重载整个页面(仅刷新页面的局部)的模式------在形式和功能上模拟桌面应用程序。

就目前而言,编写应用程序时有两种基本的选择:

  1. 桌面应用程序
  2. WEB应用程序

桌面应用程序通常从其他网站下载,安装到您的计算机上。桌面应用程序可能使用互联网下载更新,但运行这些应用程序的代码在您的桌面计算机上。

WEB应用程序运行在互联网的某台WEB服务器上,要通过WEB浏览器访问这种应用程序。

桌面应用程序一般很快,就在您的计算机上运行,不用等待互联网的链接;web应用程序提供了在桌面上不能实现的服务(比如天猫,淘宝这种具有支付功能的网站),但是,随着web的强大而出现的是等待,等待服务器的响应,等待屏幕的刷新和等待请求返回和生成新的页面。


XMLHttpRequest的一个优点在于它易于使用,我们只需创建对象,发送请求,等待服务器的响应即可。

ajax是由html,javascript技术,DHTML和DOM组成。

应用Ajax的例子

QQ空间:

你不停的拉滚动条,动态就一直在更新内容,页面没有刷新。

百度搜索:

你开始输入数据时,一个下拉框就会显示你可能干兴趣的与搜索关键字相关的条目。



浏览器支持:

IE5+

FF1+

Opera9+

Safari2+

Chrome1+


创建XMLHttpRequest对象

所有浏览器都有javascript对象XMLHttpRequest。

XMLHttpRequest对象起源于一个Microsoft组件XmlHttp,组件在库MSXML(Active XML解析器)中随IE5发布,该组件为开发人员提供了一种打开HTTP连接并检索XML数据的简单方法。尽管XMLHttpRequest对象名称包含有XML,但可以使用它获取其他类型的数据。


跨浏览器问题:

浏览器对XMLHttpRequest对象的支持分两种,ActiveX(ie5/6)和内置支持(其他浏览器)。这两类浏览器仅在创建XMLHttpRequest对象时有区别,在创建后,其余的代码则兼容各种浏览器。

第一类:使用ActiveX(IE5/6)

因为XMLHttpRequest对象来源于库MSXML(Active XML解析器),所以在这些浏览器中实例化XMLHttpRequest对象,就需要创建一个ActiveX对象。

var myhttp=new ActiveXObject("Microsoft.XMLHttp");


这行代码创建Microsoft的XMLHttpRequest对象的第一个版本。Microsoft的XmlHttp对象还有其他许多的版本。后面的例子将给出使用最新版本的方式。


第二类:调用内置的构造函数(IE7+,其他浏览器)

var myhttp=new XMLHttpRequest();


这行代码创建的XMLHttpRequest对象没有不同的版本,只需调用构造函数,就能创建和使用XMLHttpRequest对象。


 兼容:一个函数创建适用于所有的浏览器的对象。

function createXmlHttpRequest(){
       if (window.XMLHttpRequest) 
    {

        var oHttp = new XMLHttpRequest();
        return oHttp;

    } 
    else if (window.ActiveXObject) 
    {

        var versions = 
        [
            "MSXML2.XmlHttp.6.0",
            "MSXML2.XmlHttp.3.0"
        ];

        for (var i = 0; i < versions.length; i++) 
        {
            try 
            {
                oHttp = new ActiveXObject(versions[i]);
                return oHttp;
            } 
            catch (error) 
            {
              //do nothing here
            }
        }

    }

    return null;
}


首先检查是否存在window.XMLHttpRequest,如果存在,函数即使用XMLHttpRequest构造函数来创建XMLHttpRequest对象(函数遇到return语句退出函数不再执行函数后面的代码)。否则,就检测用于ie5/6的window.ActiveXObject,并尝试用最新的XmlHttp版本创建对象。如果所有的情况都不能创建XMLHttpRequest对象,那么就返回null。

注意:测试顺序很重要。先测试window.XMLHttpRequest对象,是因为ie7支持window.XMLHttpRequest和window.ActiveXObject。


使用XMLHttpRequest对象

一旦创建了XMLHttpRequest对象,就可以准备用它来请求数据。该对象提供的方法和属性,都与发送请求及处理响应有关。XMLHttpRequest对象唯一的目的就是让您发送请求和接收响应,其他一切都是javascript,css或者页面中的其他代码的工作:改变用户界面,切换对象,解析服务器返回的数据。


第一步,open()方法

 初始化该XMLHttpRequest对象,接受三个参数:

myHttp.open(requestType,url,boolean)

第一个requestType表示请求类型的字符串,其值可以是GET或者POST.
第二个参数是要作为请求发送目标的URL。
第三个参数是true或false,表示请求是以异步还是同步的模式发出。
  • false同步模式发出的请求会暂停所有javascript代码的执行,知道服务器获得响应为止,如果浏览器在连接网络时或者在下载文件时出了故障,页面就会一直挂起。 
  • true:异步模式发出的请求,请求对象收发数据的同时,浏览器可以继续加载页面,执行其他javascript代码。
myHttp.open("GET","http://localhost/myfile.txt",false);


注意:首选异步。
 

第二步,send()方法:

使用该方法发送请求。只接受一个参数,参数是一个字符串,包含随请求一起发送的请求体。GET请求不包含任何信息,所以把null传送为参数(GET请求把要发送给服务器的数据写在请求url中,所以send()中不需要发送任何数据。)。

var myHttp=createXmlHttpRequest();
myHttp.open("GET","http://localhost/myfile.txt",false);
myHttp.send(null);

这段代码发出一个GET请求,以同步模式获取myfile.txt文件。调用send()方法会把该请求发送给服务器。

虽然可以使用send()发送数据,但也能通过URL本身发送数据。事实上,GET请求(在ajax应用中占80%)中,用URL发送数据要容易的多。如果要发送安全信息或XML,可以考虑send()发送内容。如果不需要send()传递数据,则只要传递null作为该方法的参数即可。

注意send()方法必须接收一个参数,甚至可以是null。


status属性:

如果服务器响应请求并完成了处理但是报告了一个错误怎么办?不管是页面不存在404,或者访问数据收到保护404或禁止访问401.无论哪种情况,这些错误码都是从完成的响应中得到的。换句话说,服务器履行了请求(http就绪状态是4)但是没有返回客户机预期的数据。

每个XMLHttpRequest对象独有status属性,该属性包含了与服务器的响应一起发送回来的http状态码。服务器返回状态码200表示请求成功,404表示找不到请求的文件。


var myHttp=createXmlHttpRequest();
	myHttp.open("GET","http://localhost/myfile.txt",false);
	myHttp.send(null);
	if(myHttp.status==200){
		alert("############");
	}
	else if(myHttp.status==404){
		alert("$$$$$$$$$$$$$$$");
	}
	else{
		alert(myHttp.status);
	}


这段代码通过检查status属性,确定给用户显示什么信息。如果返回的状态码为200即表示存在请求文件,404则请求文件不存在。

HTTP状态码有很多,因此不可能检查每个状态码,大多数情况下,我们只需要关心请求是否成功即可(状态码200)。

精简代码:

var myHttp=createXmlHttpRequest();
	myHttp.open("GET","http://localhost/myfile.txt",false);
	myHttp.send(null);
	if(myHttp.status==200){
		alert("success");
	}
	else{
		alert("error"+myHttp.status);
	}


http状态码

从web出现以来,它们就已经存在了。在web浏览器中您可能经常看到过这几个状态码:
  • 401:未经授权
  • 403:禁止访问
  • 404:没有找到网页
在很多ajax应用程序中,您将看到一个回掉函数,它负责检查就绪状态,然后继续利用从服务器响应中返回的数据。但是种做法往往是不可取的会带来一些问题。比如,服务器脚本需要认证,而请求没有提供有效的证书,那么服务器就会返回403或401之类的错误代码,然而这时,由于服务器已经对请求做了应答,因此就绪状态就被设置了4(即使应答不是请求所期望的)。最终,用户没有获得有效的数据,当javascript试图使用不存在的服务器数据时就可能会出现错误。


异步请求

http就绪状态

前面事例演示了同步请求的简洁性,而异步请求会给代码添加一些复杂性。因为异步请求必须处理readystatechange事件。在异步请求中,XMLHttpRequest对象提供了readyState属性,该属性包含一个数值,每个值都代表请求生存期中的特定状态(http就绪状态)。如:

  • 0:已经创建对象,但还没有调用open()方法。
  • 1:已经调用open()方法,但还没有发送请求。
  • 2:请求已经发送,标题和状态已经收到,并可用。
  • 3:接收到来自服务器的响应。
  • 4:接收完请求数据,表示已经完成请求。

与大多数浏览器兼容问题一样,这些状态的使用都不一致。也许你期望值是从0,1,2,3,4变化的,但实际上,一些浏览器从不报告0或者1而直接从2开始,然后3和4.还有一些浏览器则报告所有的状态,0,1,2,3,4.还有一些则多次报告1.

IE7+:1,2,3,4 。没有0。
其他浏览器:2,3,4 。没有0和1。

就绪状态中的readyState属性为0,表示的是未初始化状态。一旦对请求对象调用open()之后,这个属性就被设置为1.由于您通常都是在一对请求进行初始化之后就立即调用open()。因此很少会看到readyState==0的状态。另外,未初始化的就绪状态在实际的应用程序中是没有真正的用处的。
要看到readyState==0的状态就必须在调佣open()之前查看就绪状态。

正在处理的请求的就绪状态,除了0就绪状态之外,请求对象还需要依次经历典型的请求和响应的其他几个钟就绪状态,最后才以就绪状态4的形式结束(确保了服务器已经完成对请求的处理)。


注意:当0等于4时,相同的XMLHttpRequest对象使用在多个函数之间生成多个请求时候。
当多个javascript函数都使用相同的请求对象时,你需要检查就绪状态0来确保这个请求对象没有正在使用。由于readyState==4表示一个已完成的请求,因此您经常会发现那些目前没有在使用的处于就绪状态的请求对象仍然被设置成了4-----这是因为从服务器返回来的数据已经使用过了。但是从他们被设置为4之后就没有进行任何变化。有一个函数abort()会重新设置请求对象,但是这个函数却不是真正为了这个目的而使用的。如果你必须使用多个函数,最好是为每个函数都创建对象并使用一个函数,而不是在多个函数之间共享相同的对象。




每当 readyState属性发生变化时,都会触发readystatechange事件,调用onreadystatechange事件处理程序。

onreadystatechange允许指定一个回调函数,回调函数允许服务器反向调用web页面中的代码。 当服务器完成请求之后,会查看 XMLHttpRequest对象,特别是onreadystatechange属性,然后调用该属性指定的任何方法,无论网页本身在做什么。这就称为异步的原因,用户在操作页面,而服务器在响应请求并触发onreadystatechange属性指定的回调方法。
前面提到的当服务器完成请求之后会在XMLHttpReques的onreadystatechange属性中查找要调用的方法。这是真的,但这说的不完整。事实上,每当http就绪状态改变(readyState属性发生变化)时服务器都会调用该方法。

注意onreadystatechange属性要放在send()方法之前。

   var myHttp=createXmlHttpRequest();
    function myHttp_readyStateChange(){
    	   if(myHttp.readyState==4){
            if(myHttp.status==200){
              alert(myHttp.responseText); 
            }else{
                alert("error"+myHttp.status)
            }
        }
    }
	myHttp.open("GET","http://localhost/myfile.txt",true);
    myHttp.onreadystatechange=myHttp_readyStateChange; 
	myHttp.send(null);


这段代码先利用函数createXmlHttpRequest()创建XMLHttpRequest对象,并把结果返回给变量myHttp。

然后定义一个myHttp_readyStateChange()函数,函数体,先测试readyState属性是否为4,确定请求是否完成。

接下来检查请求状态码status属性,确定服务器是否返回所请求的数据。

如果该两个条件都满足,就显示responseText 属性的值(这是XMLHttpRequest异步请求的数据)。


注意:因为即使请求成功,也有可能得不到需要的信息。在请求服务器端可能发生了错误(404,500或其他错误)。因此,仍然需要检查请求的状态码。一般在检查了readyState属性后还要检查下status状态码。


读取响应数据

XMLHttpRequest对象属性--------responseText

确保请求已经处理完成(就绪状态4),服务器给出了正常的响应(状态码200),那么我们就可以处理服务器返回的数据了。返回的数据保存在XMLHttpRequest对象的responseText属性中。返回的文本以什么形式,逗号分隔的值,管道符(|)分隔的值,还是返回长文本字符,都由服务器决定。

返回XMLHttpRequest异步请求的数据,responseText返回的是纯文本。

var response = request.responseText.split("|");
         document.getElementById("order").value = response[0];
         document.getElementById("address").innerHTML =
          response[1].replace(/\n/g, "");


将得到的responseText使用javascript中的split()方法从管道符号进行分隔,得到的数组放到变量response中,那么就可以访问里面的内容了,数组中的第一个值通过response[0]获得。


与就绪状态类似,responseText属性的值在整个请求的生命周期中也会发生变化。不同浏览器之间这种变化还不尽相同。

ie7+:状态为2时开始有值。

其他浏览器:状态为3时开始有值。

测试responseText属性的值的代码:

    function myHttp_readyStateChange(){
      alert(myHttp.responseText);//查看responseText变化
        if(myHttp.readyState==4){
            if(myHttp.status==200){
               // alert(myHttp.responseText); //responseText 返回的仅仅是你PHP代码执行完,在浏览器输出什么,他就返回什么的.如果它直接返回是PHP代码,那么说明你的PHP文件没有执行,查看服务器是否正常运行了PHP文件
               if(myHttp.responseText=="available"){
                 alert("你的用户名"+unameValue+"可以用")
               }
               else{
                alert("你的用户名"+unameValue+"不可以用")

               }
            }else{
                alert("error"+myHttp.status)
            }
        }

    }
}

在所有的文档和规范中都强调,只有在就绪状态为4的时候数据才可以安全使用,上面的测试中我们可以知道就算在状态为3时也是可以得到完整的数据。但是最好还是在4的时候才获取数据。比较好的做法是是向用户提供一些反馈,说明处于就绪3时,很快就有响应了------可以在就绪状态发生变化时更新一些数据,比如,进度条,状态1时25%,状态2时50%,状态3时75%,状态4时100%(完成)。但是,在不同的浏览器中状态值是不同的。


XMLHttpRequest对象属性----------responseXML

如果服务器使用XML响应则需要用该属性来处理响应。

实践

Ajax验证表单字段实现在表单提交之前,把数据发送给服务器----允许服务器验证数据并告诉用户验证结果,而无须重载页面。

因为Ajax是浏览器和服务器之间的通信,所以需要一个简单的服务器应用程序,本例将通过从PHP中请求数据,并给javascript发回响应。


例子:

准备工作


把相关文件放到服务器中并启动服务器,然后浏览器http打开HTML文件输入测试

这里通过PHP验证用户名和邮箱。


PHP文件formvalidator.php


<?php

header("Content-Type: text/plain");
header("Cache-Control: no-cache");

$array = Array(
    Array("jmcpeak", "someone@zyx.com"),
    Array("pwilton", "someone@xyz.com")
);

if (isset( $_GET["username"]) || isset( $_GET["email"])) 
{
	$result = false;

	if (isset( $_GET["username"])) 
	{
		$searchTerm = $_GET["username"];
	    
		for ($i = 0; $i < count($array); $i++) 
		{
			if (strtolower($array[$i][0]) == strtolower($searchTerm)) 
			{
				$result = true;
			}
		}
	}

	if (isset( $_GET["email"])) 
	{
		$searchTerm = $_GET["email"];
	    
		for ($i = 0; $i < count($array); $i++) 
		{
			if (strtolower($array[$i][1]) == strtolower($searchTerm)) 
			{
				$result = true;
			}
		}
	}

	$strResult = ($result)?"not available":"available";

	echo $strResult;
} 
else 
{
	echo "php是正常工作。恭喜你!";
}
?>


分析该php文件只要不是数组$array里面的用户名那么就输出available否则输出not available。第一个if语句通过get方式得到username的参数值,然后和数组里的用户名比较。相等的话$result设置为true那么返回给html的结果就是not available(不可用)。


以上PHP文件提供测试username和email。检查用户名Http://本机IP(服务器)/formvalidator.php?username=要查询的用户名;检查邮箱Http://本机IP(服务器)/formvalidator.php?email=要查询的邮箱;

下面给出HTML文件

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<script>
function checkUname(){
    var unameValue=document.getElementById("uname").value;
    if(unameValue==""){
        alert("请输入用户名");
        return;
    }
     var myHttp=createXmlHttpRequest();
     myHttp.open("GET","formvalidator.php?username="+unameValue,true);
    myHttp.onreadystatechange=myHttp_readyStateChange; /*每次readyState属性值发生变化的时候就调用myHttp_readyStateChange()方法*/

    myHttp.send(null);
  

    function myHttp_readyStateChange(){
        if(myHttp.readyState==4){
            if(myHttp.status==200){
               // alert(myHttp.responseText); //responseText 返回的仅仅是你PHP代码执行完,在浏览器输出什么,他就返回什么的.如果它直接返回是PHP代码,那么说明你的PHP文件没有执行,查看服务器是否正常运行了PHP文件
               if(myHttp.responseText=="available"){
                 alert("你的用户名"+unameValue+"可以用")
               }
               else{
                alert("你的用户名"+unameValue+"不可以用")

               }
            }else{
                alert("error"+myHttp.status)
            }
        }

    }
}
function createXmlHttpRequest(){
       if (window.XMLHttpRequest) 
    {

        var oHttp = new XMLHttpRequest();
        return oHttp;

    } 
    else if (window.ActiveXObject) 
    {

        var versions = 
        [
            "MSXML2.XmlHttp.6.0",
            "MSXML2.XmlHttp.3.0"
        ];

        for (var i = 0; i < versions.length; i++) 
        {
            try 
            {
                oHttp = new ActiveXObject(versions[i]);
                return oHttp;
            } 
            catch (error) 
            {
              //do nothing here
            }
        }

    }

    return null;
}
</script>
<body>
	<form>
		<table>
			<tr>
				<td>用户名:</td>
				<td><input type="text" id="uname"></td>
				<td><a href="javascript:checkUname()">验证</a></td>
			</tr>
			<tr>
				<td>邮箱:</td>
				<td><input type="text" id="email"></td>
				<td><a href="javascript:checkEmail()">验证</a></td>
			</tr>
		</table>
	</form>
</body>
</html>

表单中的用户名后面的验证被点击就执行checkUname()函数,首先获得ID为uname的值,判断是否为空,空则提醒输入并退出函数不执行后面的代码。条件不符合(不为空)则执行后面的代码。通过函数createXmlHttpRequest()创建XMLHttpRequest对象并赋值给变量myHttp, 调用open()函数初始化XMLHttpRequest对象 GET方式传递参数 ,接收地址是formvalidator.php, true采取异步模式。最后通过send()函数发出请求,前面说过该函数一定要包含参数,实在没有就给个null。   

前面说过采取异步请求的话XMLHttpRequest对象会提供readyState属性,该值包含一个数值,代表了不同的请求状态,每当readyState属性发生变化的时候都会触发onreadystatechange事件处理程序。这里把myHttp_readyStateChange里的代码赋值给onreadystatechange事件处理程序,那么每次readyState属性发生变化时都执行等号右边的代码。

createXmlHttpRequest()函数:根据不同的浏览器创建XMLHttpRequest对象;

myHttp_readyStateChange()函数:根据服务器传回来的信息决定该如何显示给用户。

注意:responseText 返回的仅仅是你PHP代码执行完所输出的代码,它在浏览器输出什么,就返回什么的.如果它直接返回是PHP代码,那么说明你的PHP文件没有执行,查看服务器是否正常运行了PHP文件


测试:

随意输入jmcpeak,pwilton意外的字符点击验证:


清空文本框后输入pwilton点击验证:


上面利用Ajax的用法的步骤可以总结为:

创建XMLHttpRequest对象----------open()初始化对象---------send()发出请求----------处理请求返回信息responseText,但是具体的步骤还是按照下面给出的步骤为好。


以后都用这样的方式用Ajax显得有的麻烦了,现在我们把运用Ajax的关键代码封装起来,方便我们以后用吧。

封装Ajax:创建简单的Ajax模块

使用ajax的步骤:

在所有ajax应用程序中基本都雷同的流程:

  1. 从web表单中获取需要的数据。
  2. 建立要链接的url。
  3. 打开到服务器的链接。
  4. 设置服务器在完成后要运行的函数。
  5. 发送请求


在程序设计中,代码重用的概念非常重要。

编写自定义的Ajax模块XMLHttpRequest使异步请求更易于使用和管理。


function HttpRequest(sUrl, fpCallback) 
{
    this.request = this.createXmlHttpRequest();
    this.request.open("GET", sUrl, true);

    var tempRequest = this.request;
    function request_readystatechange() 
    {
        if (tempRequest.readyState == 4) 
        {
            if (tempRequest.status == 200) 
            {
                fpCallback(tempRequest.responseText);
            } 
            else 
            {
                alert("An error occurred while attempting to contact the server.");
            }
        }
    }
    
    this.request.onreadystatechange = request_readystatechange;
}

HttpRequest.prototype.createXmlHttpRequest = function () 
{
    if (window.XMLHttpRequest) 
    {

        var oHttp = new XMLHttpRequest();
        return oHttp;

    } 
    else if (window.ActiveXObject) 
    {

        var versions = 
        [
            "MSXML2.XmlHttp.6.0",
            "MSXML2.XmlHttp.3.0"
        ];

        for (var i = 0; i < versions.length; i++) 
        {
            try 
            {
                oHttp = new ActiveXObject(versions[i]);
                return oHttp;
            } 
            catch (error) 
            {
              //do nothing here
            }
        }

    }

    return null;
}

HttpRequest.prototype.send = function () 
{
    this.request.send(null);
}

定义HttpRequest(sUrl, fpCallback) 构造函数:

接受两个参数,sUrl是XMLHttpRequest对象的请求URL。fpCallback是一个回调函数,当收到服务器的响应时(请求的readyState为4,status为200)调用这个回调函数。

构造函数的一行代码初始化request属性,执行createXmlHttpRequest()函数并返回给它一个XMLHttpReques对象。

然后用XMLHttpReques对象的open()方法初始化请求对象。该方法将请求类型设置为GET,使用sUrl参数指定要请求的URL,并把请求对象设置为使用异步方式。

接下来定义了request_readystatechange()函数。

在一个函数中定义另一个函数,称为:“闭包”。“闭包”(这里指request_readystatechange()函数)不能在容器函数(这里指构造函数)以外访问,但内部函数可以访问其容器函数的变量和参数。

在这函数前定义了tempRequest变量,这个变量指向当前对象的request属性的指针。

fpCallback(tempRequest.responseText); 这行代码调用了构造函数fpCallback()参数定义的回调函数(下面例子中是fun1()),将responseText属性传递给该回调函数。这样回调函数(fun1())就可以使用从服务器中接收的信息。就是让传进来的函数可以使用responseText属性。


下面的我用上面的代码写一个例子

<script>
    function fun1(srespon){
        alert(srespon)

    }
    var myhttp=new HttpRequest("http://mytext.txt",fun1);
    myhttp.send();
</script>


fun1()函数将作为HttpRequest()函数的参数传入,主要是用以如何处理服务器返回的信息。这里是弹出。

然后执行HttpRequest()函数,参数为要请求的服务器地址和处理服务器返回的信息的函数,然后就交给前面定义好的函数处理,结果是返回XMLHttpReques对象给变量myhttp.


上面的模块封装了与异步XMLHttpRequest请求相关的代码,不需要创建请求对象,处理readystatechange事件,或者检查请求状态,因为封装好的HttpRequest()函数会完成所有这些工作。


现在我们用前面创建的Ajax模块来做前面的表单例子

代码:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<script>
function checkUname(){
	var uname=document.getElementById("uname").value;
	if(uname==""){
		alert("请输入用户名");
	}
	var myhttp=new HttpRequest("formvalidator.php?username="+uname,fun01);
	myhttp.send();
}


function fun01(r){
	if(r=="available"){
		alert("该用户可用")
	}else{
		alert("该用户不可用")
	}

}
	

	function HttpRequest(sUrl, fpCallback) 
{
    this.request = this.createXmlHttpRequest();
    this.request.open("GET", sUrl, true);

    var tempRequest = this.request;
    function request_readystatechange() 
    {
        if (tempRequest.readyState == 4) 
        {
            if (tempRequest.status == 200) 
            {
                fpCallback(tempRequest.responseText);
            } 
            else 
            {
                alert("An error occurred while attempting to contact the server.");
            }
        }
    }
    
    this.request.onreadystatechange = request_readystatechange;
}

HttpRequest.prototype.createXmlHttpRequest = function () 
{
    if (window.XMLHttpRequest) 
    {

        var oHttp = new XMLHttpRequest();
        return oHttp;

    } 
    else if (window.ActiveXObject) 
    {

        var versions = 
        [
            "MSXML2.XmlHttp.6.0",
            "MSXML2.XmlHttp.3.0"
        ];

        for (var i = 0; i < versions.length; i++) 
        {
            try 
            {
                oHttp = new ActiveXObject(versions[i]);
                return oHttp;
            } 
            catch (error) 
            {
              //do nothing here
            }
        }

    }

    return null;
}

HttpRequest.prototype.send = function () 
{
    this.request.send(null);
}

</script>
<body>
	<form>
		<table>
			<tr>
				<td>用户名:</td>
				<td><input type="text" id="uname"></td>
				<td><a href="javascript:checkUname()">验证</a></td>
			</tr>
			<tr>
				<td>邮箱:</td>
				<td><input type="text" id="email"></td>
				<td><a href="javascript:checkEmail()">验证</a></td>
			</tr>
		</table>
	</form>
</body>

</html>

一个javascript方法捕捉用户输入表单的信息并将其发送给服务器,另一个javascript方法监听和处理响应。所有的这些实际上都依赖于第一个javascript方法( checkUname()方法),它启动了整个过程。当然你可以在用户修改表单值得时候触发该函数onChange="checkUname()".


首先把前面创建好的Ajax模块函数HttpRequest()复制进去。模块函数中包含了创建XMLHttpRequest对象,open方法.send()方法还需调用。

然后new通过HttpRequest()函数创建出一个XMLHttpRequest对象返回给变量myhttp。该HttpRequest()函数需要来个参数,要请求的地址和一个处理返回信息的函数(一个函数作为另一个函数的参数称为回调函数),然后通过send()方法发出请求,这时不用传入参数是因为在模块HttpRequest()函数中早已封装。

var myhttp=new HttpRequest("formvalidator.php?username="+uname,fun01);
myhttp.send();

最后定义一下要做为模块HttpRequest()函数参数的回调函数fun01(),该回调函数在模块HttpRequest()函数中需要一个参数来代表responseText(服务器返回的信息)。fun01()的函数体是根据返回的值做出了一些处理。


这里的运用模块HttpRequest()函数的顺序可以总结为:

用new和HttpRequest创建XMLHttpRequest对象----------然后send()出去---------然后处理回调函数


在请求和响应中使用XML

XML是编程中最常用的数据格式之一。在用Ajax的时候你也许也注意到了ajax中的x并且也知道它代表的是XML。还有就是其核心对象的名称-------XMLHttpRequest.所以很多人会认为XML是ajax的核心技术之一,有的甚至认为XMLHttpRequest对象只能使用XML。但我想说的是XML确实有应用在ajax中,而且XMLHttpRequest也支持这种用法。但是使用ajax不是一定要使用XML。XMLHttpRequest也只是一个名称。还有就是XML是一种数据格式而不是传输协议。

使用XML

在异步应用程序中XML有两种基本用法:

  1. 以XML格式从网页向服务器发送请求;
  2. 以XML格式在网页中从服务器中接收请求。
第一种用法,即用XML发送请求,需要将请求的格式设置为XML,主要的任务是通过构建既符合XML规则又能被服务器理解的方式构造请求。
第二种用法,需要从服务器上接收响应,然后从XML中提取数据。

注意:XML不是一种简洁,快速和节省空间的格式,和文本相比,XML通常会占用更多额空间,速度也更慢,因为需要再消息中增加XML所需要的标签和语义。

从客户机到服务器的XML


假设有:name=rose

使用普通的文本向服务器发送名/值对

在90%的web应用程序中都会用名/值发送到服务器。

var unameValue=document.getElementById("name").value;
var myHttp=createXmlHttpRequest();
url="a.php?username="+escape(unameValue);
myHttp.open("GET",url,true);
myHttp.onreadystatechange=myHttp_readyStateChange; 
myHttp.send(null);

向服务器发送XML

将名/值对转化为XML

如果你想使用XML作为数据格式,首先要做的是找到一种基本XML格式来存储数据。显然,名/值对可以全部转化成XML元素,以其中的名称作为元素名,值作为元素内容。

<people>
  <name>rose</name>
</people>

在网络上传输XML之前,要保证服务器以及发送数据的脚本能接受XML,很多人认为只要通过网络发送XML就能被服务器正确的接收和解析。

实际上,需要注意两点来保证发送的XML数据能被正确的接收:

  1. 保证向其发送XML的脚本能接受XML数据格式。
  2. 保证脚本发送的XML数据所采用的格式和结构符合服务的要求的格式和结构。
假设服务器接受以下格式的数据:
<address>
<firstname>rose</firstname>    
</address>

看起来和上面的XML类似,但有两点不同:
  1. 来自客户机的XML要求封装在address元素中而不是封装在people元素中。
  2. 来自客户机的XML的名字要求使用firstname元素而不是使用name元素。
必须明确服务器期望的格式,并把要发送的数据塞进那种格式。否则,服务器就会就收失败或者在页面上显示错误信息。
使用XML发送名/值对
当向服务器发送XML时,更多的代码是用于获取数据和包装成XML,而不是真正的传输数据。实际上,只要准备好发送到服务器的XML字符串,发送工作就和普通文本一样了。
function sendXML(){
var firstname=document.getElementById("firstname").value;
var xmlString="<address>"+"<firstname>"+firstname+"</firstname>"+"</address>"
var myHttp=createXmlHttpRequest();
url="test.php"
myHttp.setRequestHeader("Content-Type","text/xml");
myHttp.onreadystatechange=myHttp_readyStateChange;
myHttp.send(xmlString);
}

请求中的数据必须手工格式化为XML。建立XML之后,按照和发送文本基本相同的方式打开连接。对于XML最好使用POST请求,因为有的浏览器限制了GET请求字符串的长度而XML可能很长。此外XML通过send()方法发送,而不是附加在请求url中。
必须编写如下一段代码
myHttp.setRequestHeader("Content-Type","text/xml");

告诉服务器要发送的是XML而不是一般的名/值对。如果要使用名/值对,对应行该是:

myHttp.setRequestHeader("Content-Type","text/plain");

注意:使用XML要花和实际数据同样多的实际来处理尖括号和标签名称,这有可能使本来很少的输入变得非常大。除了这些问题之外,和普通的文本以及名/值对相比,在请求中使用XML实际上没有多少好处。建议除非和只接受XML的脚本打交道,否则在请求中最好使用普通文本方式而不是XML。

需要向服务器发送XML的理由:
  1. 服务器仅接受XML请求。
  2. 您正在调用一个仅接受XML请求的远程API。这实际是上一种情况的特列。

从服务器到客户机的XML

在你发送名称/值对时,web浏览器会发送请求,平台会响应该请求,并承载一个服务器程序,配合它将那些名/值对转换成服务器程序可以轻松处理的数据。实际上,每一种服务器技术(JAVA,PHP....)都允许您调佣多种方法来根据名称获取值。
如果服务器使用字符串  name=rose&age=15(用&分隔多个参数)应答一个应用程序,客户机没有提供任何标准的方法来将每个对拆分成名称和值,所以你必须手动解析所返回的数据。如果服务器返回一个由名/值对构成的响应,这样的响应解析难度和使用分号,竖线或其他任何分隔符相同。
对你来说,这就意味着没有任何简单的方法在响应中使用纯文本,使客户机以一种标准的方法获取并解析响应,你需要利用一切手段(分号,等号,竖线等)来拆分这些响应字符,这些都要你自己手动操作,比较麻烦。

注意:在绝大多数HTTP请求中,转义序列%20用于表示一个空格,文本“name rose"将以”name%20rose“的形式通过http发送。

进入XML

基于以上原因,使用XML的原因就显而易见了。
向服务器发送数据时,名称/值对是非常好的选择,因为服务器和服务器端语言可以轻松解析名/值对。
向客户机返回数据时使用XML也是如此,有几种方法可以用服务器中获得XML响应,并使用较为标准的代码提取数据。

从服务器接收XML

有两种基本的方式处理一个来自服务器的XML响应:

  1. 被格式化为XML的纯文本
  2. XML文档,由一个document对象表示

如:

<address>
  <show>
    <firstname>rose</firstname>
    <place>hk</place>  
  </show>  
  <show>
     <firstname>rose02</firstname>
     <place>hk02</place>  
  </show>  
  <show>
     <firstname>rose03</firstname>
     <place>hk03</place>  
  </show>  
</address>

将XML作为纯文本(无格式的纯文本处理)

处理XML最简单的选择就是用于处理服务器返回的其他文本片段相同的方法来处理它,也就是说,基本上就是忽略数据格式,只关注服务器的响应。

这种情况下,你要使用请求对象的是responseText属性,就像服务器返回的不是XML响应时一样。

        if(myHttp.readyState==4){
            if(myHttp.status==200){
          var text=myHttp.responseText;
            }else{
                alert("error"+myHttp.status)
            }
        }


最终,你将得到把所有一切串联在一起的XML响应,位于变量text中,如果你输出此变量,代码是连续的一个代码行。

<address><show><firstname>rose</firstname><place>hk</place></show><show><firstname>rose02</firstname><place>hk02</place></show><show><firstname>rose03</firstname><place>hk03</place></show></address>


大多情况下,服务器不会使用空格和回车来格式化XML,而是将一切串联在一起,当然你的应用程序不会过于在意空格,所以这算不了什么问题,只是会使代码阅读起来有些困难。

在这里可以用javascript split()函数来拆分此数据,并通过基本的字符串操作来获得元素的名称和值,毫无疑问这是个令人头疼的过程,你不应该选择这种方法来获得XML数据。

将XML当成XML(使用dom处理)

尽管可以如上所述将服务器的XML格式的响应视同为其他任何文本响应来处理,但这样做没有很好的理由。你可以使用dom操作XML,javascript中的XMLHttpRequest对象提供了一个属性(responseXML属性),可以更好地获取服务器的XML响应,并且是以DOM document对象的形式获取它。

现在我们用的不是responseText属性,而是responseXML属性,该属性在XMLHttpRequest对象上可用,它以DOM文档的格式返回服务器的响应。

if(myHttp.readyState==4){
if(myHttp.status==200){
var xmldoc=myHttp.responseXML;
}else{
  alert("error"+myHttp.status)
 }
 }

现在你就有了一个DOM  document。

比如我要获得所有的show元素:

var showelement=xmldoc.getElementsByTagName('show')

现在你可以使用你所了解的全部DOM方法,轻松操纵从服务器处接收到的XML。

解析XML的其他可选方法

除了将XML作为无格式的文本处理或使用DOM处理之外,还有一种选择,那就是JSON,只要提到XML和ajax应用程序,你就可能会听到JSON。

大体上,可以用JSON做的事,用DOM都可以做,反之亦然。






处理延迟

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<script>
	var myhttp=new  HttpRequest("http://localhost/file.txt",fun01);
	document.getElementById("divloading").style.display="block";
	myhttp.send();
	function fun01(r){
		document.getElementById("divloading").style.display="none";
	}
	</script>
</body>
</html>

这段代码利用前面的HttpRequest()函数创建请求。在发送前,在文档中获取id为divloading的html元素,这个元素将告诉用户,数据正在加载,请求完成时就把它隐藏。



注意事项:

XMLHttpRequest对象不能访问任何不同来源的文档或文件。

ajax代码(具体来说就是XMLHttpRequest对象)只能对所在的同一个域发送请求。也就是说在本地机器上运行的代码只能对本地机器上的服务器端脚本发送请求。、


如:

http://www.mysite.com/page1.html

http://www.mysite.com/page2.html

这是两个同源页面,它们有相同的主机(www.mysite.com),相同的协议(http)相同的端口(80)。因为这两个页面是同源页面,所以其中一个页面的javascript可以访问另一个页面。

再如:

http://www.mysite.com/page1.html

https://www.mysite.com/page2.html

主机相同,协议不同一个http,另一个是https.端口不同一个是80另一个是443.所以任何一个页面都不能访问另一个页面。


XMLHttpRequest对象不能访问任何不同来源的文档或文件。这个问题很容易解决:使用一个同源的页面作为代理(proxy),获取另一个非同源服务器上的数据。

同源策略还影响到frame/iframe技术,即两个不同来源的页面位于同一个框架集中,javascript也就无法与这两个页面交互。




最后:

总结下发出请求的步骤

接下来就是在所有 Ajax 应用程序中基本都雷同的流程:

  1. 从 Web 表单中获取需要的数据。
  2. 建立要连接的 URL。
  3. 打开到服务器的连接。
  4. 设置服务器在完成后要运行的函数。
  5. 发送请求。
下面的示例 Ajax 方法就是按照这个顺序组织的:

function callServer() {

  var city = document.getElementById("city").value;
  var state = document.getElementById("state").value;

  if ((city == null) || (city == "")) return;
  if ((state == null) || (state == "")) return;

  var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state);

  xmlHttp.open("GET", url, true);

  xmlHttp.onreadystatechange = updatePage;

  xmlHttp.send(null);
}

记住,XMLHttpRequest 惟一的目的是让您发送请求和接收响应。其他一切都是 JavaScript、CSS 或页面中其他代码的工作:改变用户界面、切换图像、解释服务器返回的数据。


重定向和重新路由

我们来讨论下在ajax中不需要考虑的问题----重定向。在http状态码中,这是300系列的状态码。包括:

  • 301:永久移动。
  • 302:找到(请求被重新定向到另一个url/uri上)。
  • 305:使用代理(请求必须使用一个代理来访问所请求的资源)
为什么说我们不需要关心重定向问题?因为:
  1. ajax应用程序通常都是为一个特定的服务器端脚本,servlet或应用程序而编写的,因此有时你会知道资源已经移动了,接下来只需修改请求中的url即可。
  2. ajax只能对所在的同一个域发送请求,也就是说提供生成ajax请求的web页面的域必须是对这些请求进行响应的域名。因此 ebay.com 所提供的 Web 页面就不能对一个在 amazon.com 上运行的脚本发出Ajax请求;在 ibm.com 上的 Ajax 应用程序也无法对在 netbeans.org 上运行的 servlets 发出请求。
所以,您的请求是无法重定向到其他服务器上的,而不会产生安全性错误。这些情况下,你根本不会得到状态代码。通常在调试控制台中都会产生一个javascript错误。因此,在对状态码进行考虑后,你就完全可以忽略重定向代码的问题了。


边界情况

只有不到5%的ajax请求需要使用2,3的就绪状态和403之类的状态码-----它们只会在一些非常特殊的情况下发生,其中遇到的都是最奇特的问题。虽然这些情况不普遍,但是这些边界情况却占据了不部分用户所碰到的问题的80%。


其他请求类型

HEAD请求

 myHttp.open("HEAD",url,true);

当你这样生成HEAD请求时,服务器不会像对GET和POST请求一样返回一个真正的响应。相反,服务器只会返回资源的头部,这包括响应中内容最好修改的时间,请求资源是否存在和很多其他有用信息。您可以在服务器处理并返回资源之前使用这些信息来了解有关资源的信息。

通过XMLHttpRequest 对象getAllResponseHeaders()可以获得所有的响应头的内容

 myHttp.open("HEAD","a.php?username="+unameValue,true);
    function myHttp_readyStateChange(){
        if(myHttp.readyState==4){
            if(myHttp.status==200){
              alert(myHttp.getAllResponseHeaders())

            }else{
                alert("error"+myHttp.status)
            }
        }


下图为从一个向服务器发出的HEAD请求的简单的ajax应用程序返回的响应头。


检查URL

您已经知道了当url不存在时就会出现404错误。但可能不是服务器的问题而可能是缺少了一个特定的脚本或servlet,所以有的时候我们希望在生成完整的GET或POST请求之前来检查这个url。

    function myHttp_readyStateChange(){
        if(myHttp.readyState==4){
            if(myHttp.status==200){
              alert("url存在");
            }else if(myHttp.status==404){
                alert("url不存在");
            }
            else{
                alert("error"+myHttp.status);
            }
        }

这段代码,服务器必须对请求进行响应,并构造一个响应来填充内容长度的响应头,因此不能节省任何处理时间。

有用的HEAD请求

HEAD非常有用的一个领域是用来查看内容的长度或内容的类型,这样可以确定是否需要发回大量的数据来处理请求,和确认服务器是否试图返回二进制数据,而不是html,文本或xml(在javascript中,这三种类型数据都比二进制数据更容易处理)。
获取响应的长度:(myHttp.getResponseHeader("Content-Length");
获取内容类型:(myHttp.getResponseHeader("Content-Type");
在很多应用程序中,生成HEAD请求并没有增加任何功能,甚至会导致速度变慢(先生成一个HEAD请求来获取有关响应的数据,然后再使用一个GET或POST请求来真正获取响应)。然而,在出现您不确定有关脚本或服务器端组件的情况时,使用HEAD请求可以获取一些基本的数据,而不需要对响应数据真正进行处理,也不需要大量的带宽来发送响应。



如果你对状态码进行控制,就可以设置应用程序来处理脚本错误,非预期的响应以及边缘问题。结果是应用程序在所有时间都可以正常工作,而仅仅是只能一切都正常的情况下运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值