典型的http通信:浏览器向服务器发出请求,服务器向客户端返回响应,浏览器重新加载页面,这种不连续的页面加载方式导致用户的体验变得杂乱,缺乏连贯性。
如:
在一般的web应用程序中,用户填写表单字段然后单击submit按钮,然后整个表单发送到服务器,服务器将它转发给处理该表单的脚本(PHP或JAVA),脚本执行完成后再发送回全新的页面。该页面返回的可能是带有已经填充某些数据的新表单的html,也可能返回的是一个确认页面。在服务器上的脚本处理该表单时和返回新页面的这段时间里,用户必须等待且屏幕是一片空白,等到服务器返回数据后再重新绘制出页面。
ajax,用户在填写表单时,数据就会发送给一些javascript代码而不是直接发送给服务器。javascript代码捕获表单数据并向服务器发送请求。此时,用户屏幕上的表单不会闪烁,消失或延迟。用户不用等待服务器的响应就可以继续输入数据,滚动屏幕和继续使用页面。然后,服务器将数据返回给javascript代码(仍然在web表单中),javascript代码决定如何处理这些数据。它可以迅速更新表单数据,让人感觉应用程序是立即完成的,表单没有提交或刷新而用户就得到了新数据。javascript甚至可以对收到的数据执行一些操作然后再发送另一个请求,完全不需要用户干预。这就是ajax的强大之处,它可以根据需要自行与服务器进行交互,用户甚至不知道幕后所发生的一切。
Ajax允许客户端javascript向服务器请求和接收数据,而无须刷新页面。这种技术允许开发人员创建不中断的应用程序,用新数据重载页面的某些部分。
Ajax表示使用javascript收发来自web服务器的数据,且无须重载整个页面(仅刷新页面的局部)的模式------在形式和功能上模拟桌面应用程序。
就目前而言,编写应用程序时有两种基本的选择:
- 桌面应用程序
- 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()方法:
myHttp.open(requestType,url,boolean)
第一个requestType表示请求类型的字符串,其值可以是GET或者POST.
- false:同步模式发出的请求会暂停所有javascript代码的执行,知道服务器获得响应为止,如果浏览器在连接网络时或者在下载文件时出了故障,页面就会一直挂起。
- true:异步模式发出的请求,请求对象收发数据的同时,浏览器可以继续加载页面,执行其他javascript代码。
myHttp.open("GET","http://localhost/myfile.txt",false);
第二步,send()方法:
var myHttp=createXmlHttpRequest();
myHttp.open("GET","http://localhost/myfile.txt",false);
myHttp.send(null);
这段代码发出一个GET请求,以同步模式获取myfile.txt文件。调用send()方法会把该请求发送给服务器。
注意: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状态码
- 401:未经授权
- 403:禁止访问
- 404:没有找到网页
异步请求:
http就绪状态
前面事例演示了同步请求的简洁性,而异步请求会给代码添加一些复杂性。因为异步请求必须处理readystatechange事件。在异步请求中,XMLHttpRequest对象提供了readyState属性,该属性包含一个数值,每个值都代表请求生存期中的特定状态(http就绪状态)。如:
- 0:已经创建对象,但还没有调用open()方法。
- 1:已经调用open()方法,但还没有发送请求。
- 2:请求已经发送,标题和状态已经收到,并可用。
- 3:接收到来自服务器的响应。
- 4:接收完请求数据,表示已经完成请求。
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发回响应。
例子:
准备工作
- web服务器 :我这里用的是一个web小软件你们可以自己下载本地PHP服务器搭建工具MiniServer(迷你WAMP)下载即用;
- PHP文件
把相关文件放到服务器中并启动服务器,然后浏览器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应用程序中基本都雷同的流程:
- 从web表单中获取需要的数据。
- 建立要链接的url。
- 打开到服务器的链接。
- 设置服务器在完成后要运行的函数。
- 发送请求
在程序设计中,代码重用的概念非常重要。
编写自定义的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有两种基本用法:
- 以XML格式从网页向服务器发送请求;
- 以XML格式在网页中从服务器中接收请求。
从客户机到服务器的XML
使用普通的文本向服务器发送名/值对
在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数据能被正确的接收:
- 保证向其发送XML的脚本能接受XML数据格式。
- 保证脚本发送的XML数据所采用的格式和结构符合服务的要求的格式和结构。
<address>
<firstname>rose</firstname>
</address>
看起来和上面的XML类似,但有两点不同:
- 来自客户机的XML要求封装在address元素中而不是封装在people元素中。
- 来自客户机的XML的名字要求使用firstname元素而不是使用name元素。
使用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请求。
- 您正在调用一个仅接受XML请求的远程API。这实际是上一种情况的特列。
从服务器到客户机的XML
进入XML
从服务器接收XML
有两种基本的方式处理一个来自服务器的XML响应:
- 被格式化为XML的纯文本
- 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 应用程序中基本都雷同的流程:
- 从 Web 表单中获取需要的数据。
- 建立要连接的 URL。
- 打开到服务器的连接。
- 设置服务器在完成后要运行的函数。
- 发送请求。
下面的示例 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:使用代理(请求必须使用一个代理来访问所请求的资源)
- ajax应用程序通常都是为一个特定的服务器端脚本,servlet或应用程序而编写的,因此有时你会知道资源已经移动了,接下来只需修改请求中的url即可。
- ajax只能对所在的同一个域发送请求,也就是说提供生成ajax请求的web页面的域必须是对这些请求进行响应的域名。因此 ebay.com 所提供的 Web 页面就不能对一个在 amazon.com 上运行的脚本发出Ajax请求;在 ibm.com 上的 Ajax 应用程序也无法对在 netbeans.org 上运行的 servlets 发出请求。
边界情况
其他请求类型
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);
}
}
这段代码,服务器必须对请求进行响应,并构造一个响应来填充内容长度的响应头,因此不能节省任何处理时间。