每日三问之javascript之Ajax与跨域

28 篇文章 0 订阅
11 篇文章 0 订阅

ajax技术能够向服务器请求额外的数据而无须卸载页面,会带来更好的用户体验。

XMLHttpRequest 对象

Ajax 技术的核心是 XMLHttpRequest 对象(简称 XHR
创建 XHR 对象
var xhr = new XMLHttpRequest();

XHR的用法 

要调用的第一个方法是 open() ,它接受 3 个参数:要发送的请求的类型("get" "post" 等)、请求的 URL 和表示是否异步发送请求的布尔值。
xhr.open("get", "example.php", false);
xhr.send(null);
这里的 send() 方法接收一个参数,即要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入 null ,因为这个参数对有些浏览器来说是必需的。调用 send() 之后,请求就会被分派到服务器。
在收到响应后,响应的数据会自动填充 XHR 对象的属性,相关的属性简介如下。
  • responseText:作为响应主体被返回的文本。
  • responseXML:如果响应的内容类型是"text/xml""application/xml",这个属性中将保存包含着响应数据的 XML DOM 文档。
  • status:响应的 HTTP 状态。
  • statusTextHTTP 状态的说明。
xhr.open("get", "example.txt", false); 
xhr.send(null); 
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 
 alert(xhr.responseText); 
} else { 
 alert("Request was unsuccessful: " + xhr.status); 
}
像前面这样发送同步请求当然没有问题,但多数情况下,我们还是要发送异步请求,才能让 JavaScript 继续执行而不必等待响应。此时,可以检测 XHR 对象的 readyState 属性,该属性表示请求/响应过程的当前活动阶段。这个属性可取的值如下。
  • 0:未初始化。尚未调用 open()方法。
  • 1:启动。已经调用 open()方法,但尚未调用 send()方法。
  • 2:发送。已经调用 send()方法,但尚未接收到响应。
  • 3:接收。已经接收到部分响应数据。
  • 4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。
var xhr = createXHR(); 
xhr.onreadystatechange = function(){ 
 if (xhr.readyState == 4){ 
 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 
 alert(xhr.responseText); 
 } else { 
 alert("Request was unsuccessful: " + xhr.status); 
 } 
 } 
}; 
xhr.open("get", "example.txt", true); 
xhr.send(null);



function createXHR(){ 
 if (typeof XMLHttpRequest != "undefined"){ 
 return new XMLHttpRequest(); 
 } else if (typeof ActiveXObject != "undefined"){ 
 if (typeof arguments.callee.activeXString != "string"){ 
 var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", 
 "MSXML2.XMLHttp"], 
 i, len; 
 for (i=0,len=versions.length; i < len; i++){ 
 try { 
 new ActiveXObject(versions[i]); 
 arguments.callee.activeXString = versions[i]; 
 break; 
 } catch (ex){ 
 //跳过
 } 
 } 
 } 
 return new ActiveXObject(arguments.callee.activeXString); 
 } else { 
 throw new Error("No XHR object available."); 
 } 
}

HTTP头部信息

每个 HTTP 请求和响应都会带有相应的头部信息
  • Accept:浏览器能够处理的内容类型。
  • Accept-Charset:浏览器能够显示的字符集。
  • Accept-Encoding:浏览器能够处理的压缩编码。
  • Accept-Language:浏览器当前设置的语言。
  • Connection:浏览器与服务器之间连接的类型。
  • Cookie:当前页面设置的任何 Cookie
  • Host:发出请求的页面所在的域 。
  • Referer :发出请求的页面的 URI
  • User-Agent :浏览器的用户代理字符串。
使用 setRequestHeader() 方法可以设置自定义的请求头部信息。
var xhr = createXHR(); 
xhr.onreadystatechange = function(){ 
 if (xhr.readyState == 4){ 
 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 
 alert(xhr.responseText); 
 } else { 
 alert("Request was unsuccessful: " + xhr.status); 
 } 
 }
}; 
xhr.open("get", "example.php", true); 
xhr.setRequestHeader("MyHeader", "MyValue"); 
xhr.send(null);
调用 XHR 对象的 getResponseHeader() 方法并传入头部字段名称,可以取得相应的响应头部信息。
而调用 getAllResponseHeaders() 方法则可以取得一个包含所有头部信息的长字符串。
var myHeader = xhr.getResponseHeader("MyHeader"); 
var allHeaders = xhr.getAllResponseHeaders();

GET请求

xhr.open("get", "example.php?name1=value1&name2=value2", true);

下面这个函数可以辅助向现有 URL 的末尾添加查询字符串参数:这个 addURLParam()函数接受三个参数:要添加参数的 URL、参数的名称和参数的值。

function addURLParam(url, name, value) { 
 url += (url.indexOf("?") == -1 ? "?" : "&"); 
 url += encodeURIComponent(name) + "=" + encodeURIComponent(value); 
 return url; 
}

 下面是使用这个函数来构建请求 URL 的示例。

var url = "example.php"; 
//添加参数
url = addURLParam(url, "name", "Nicholas"); 
url = addURLParam(url, "book", "Professional JavaScript"); 
//初始化请求
xhr.open("get", url, false);

 POST请求

xhr.open("post", "example.php", true);
function submitData(){ 
 var xhr = createXHR(); 
 xhr.onreadystatechange = function(){ 
 if (xhr.readyState == 4){ 
 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 
 alert(xhr.responseText); 
 } else { 
 alert("Request was unsuccessful: " + xhr.status); 
 } 
 } 
 }; 
 xhr.open("post", "postexample.php", true);
 xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
 var form = document.getElementById("user-info"); 
 xhr.send(serialize(form)); 
}

XMLHttpRequest 2

FormData

FormData 为序列化表单以及创建与表单格式相同的数据(用于通过 XHR 传输)提供了便利。

下面的代码创建了一个 FormData 对象,并向其中添加了一些数据。

var data = new FormData(); 
data.append("name", "Nicholas");

这个 append()方法接收两个参数:键和值,分别对应表单字段的名字和字段中包含的值。

超时设定

IE8 XHR 对象添加了一个 timeout 属性,表示请求在等待响应多少毫秒之后就终止。在给 timeout 设置一个数值后,如果在规定的时间内浏览器还没有接收到响应,那么就会触发 timeout 事件,进而会调用 ontimeout 事件处理程序。
var xhr = createXHR(); 
xhr.onreadystatechange = function(){ 
 if (xhr.readyState == 4){ 
 try { 
 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 
 alert(xhr.responseText); 
 } else { 
 alert("Request was unsuccessful: " + xhr.status); 
 } 
 } catch (ex){ 
 //假设由 ontimeout 事件处理程序处理
 } 
 } 
}; 
xhr.open("get", "timeout.php", true); 
xhr.timeout = 1000; //将超时设置为 1 秒钟(仅适用于 IE8+)
xhr.ontimeout = function(){ 
 alert("Request did not return in a second."); 
}; 
xhr.send(null);

overrideMimeType()方法 

overrideMimeType() 方法,用于重写 XHR 响应的 MIME 类型。
比如,服务器返回的 MIME 类型是 text/plain ,但数据中实际包含的是 XML 。根据 MIME 类型,即使数据是 XML responseXML 属性中仍然是 null 。通过调用 overrideMimeType() 方法,可以保证把响应当作 XML 而非纯文本来处理。
var xhr = createXHR(); 
xhr.open("get", "text.php", true); 
xhr.overrideMimeType("text/xml"); 
xhr.send(null);

进度事件

  • loadstart:在接收到响应数据的第一个字节时触发。
  • progress:在接收响应期间持续不断地触发。
  • error:在请求发生错误时触发。
  • abort:在因为调用 abort()方法而终止连接时触发。
  • load:在接收到完整的响应数据时触发。
  • loadend:在通信完成或者触发 errorabort load 事件后触发。

load事件

load 事件,用以替代 readystatechange 事件。
var xhr = createXHR(); 
xhr.onload = function(){ 
 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 
 alert(xhr.responseText); 
 } else { 
 alert("Request was unsuccessful: " + xhr.status); 
 } 
}; 
xhr.open("get", "altevents.php", true); 
xhr.send(null);

progress事件

这个事件会在浏览器接收新数据期间周期性地触发。
var xhr = createXHR(); 
xhr.onload = function(event){ 
 if ((xhr.status >= 200 && xhr.status < 300) || 
 xhr.status == 304){ 
 alert(xhr.responseText); 
 } else { 
 alert("Request was unsuccessful: " + xhr.status); 
 } 
}; 
xhr.onprogress = function(event){ 
 var divStatus = document.getElementById("status"); 
 if (event.lengthComputable){ 
 divStatus.innerHTML = "Received " + event.position + " of " + 
 event.totalSize +" bytes"; 
 } 
}; 
xhr.open("get", "altevents.php", true); 
xhr.send(null);

 跨源资源共享

CORS Cross-Origin Resource Sharing ,跨源资源共享)是 W3C 的一个工作草案,定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通。CORS 背后的基本思想,就是使用自定义的 HTTP 头部浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
Origin: http://www.nczonline.net
比如一个简单的使用 GET POST 发送的请求,它没有自定义的头部,而主体内容是 text/plain 。在发送该请求时,需要给它附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。
如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源
信息(如果是公共资源,可以回发 "*" )。例如:
Access-Control-Allow-Origin: http://www.nczonline.net

IECORS的实现

微软在 IE8 中引入了 XDR XDomainRequest )类型。这个对象与 XHR 类似,但能实现安全可靠
的跨域通信。 XDR 对象的安全机制部分实现了 W3C CORS 规范。
以下是 XDR XHR 的一些不同之处。
  • cookie 不会随请求发送,也不会随响应返回。
  • 只能设置请求头部信息中的 Content-Type 字段。
  • 不能访问响应头部信息。
  • 只支持 GET POST 请求。
XDR 对象的使用方法与 XHR 对象非常相似。也是创建一个 XDomainRequest 的实例,调 open()
方法,再调用 send() 方法。但与 XHR 对象的 open() 方法不同, XDR 对象的 open() 方法只接收两个参数:请求的类型和 URL
var xdr = new XDomainRequest(); 
xdr.onload = function(){ 
 alert(xdr.responseText); 
}; 
xdr.onerror = function(){ 
 alert("An error occurred."); 
};
xdr.open("get", "http://www.somewhere-else.com/page/"); 
xdr.send(null);
在请求返回前调用 abort() 方法可以终止请求:
xdr.abort(); //终止请求
XHR 一样, XDR 对象也支持 timeout 属性以及 ontimeout 事件处理程序。
var xdr = new XDomainRequest(); 
xdr.onload = function(){ 
 alert(xdr.responseText); 
}; 
xdr.onerror = function(){ 
 alert("An error occurred."); 
}; 
xdr.timeout = 1000; 
xdr.ontimeout = function(){ 
 alert("Request took too long."); 
}; 
xdr.open("get", "http://www.somewhere-else.com/page/"); 
xdr.send(null);

其他浏览器对CORS的实现

var xhr = createXHR(); 
xhr.onreadystatechange = function(){ 
 if (xhr.readyState == 4){ 
 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 
 alert(xhr.responseText); 
 } else { 
 alert("Request was unsuccessful: " + xhr.status); 
 } 
 } 
}; 
xhr.open("get", "http://www.somewhere-else.com/page/", true); 
xhr.send(null);

Preflighted Reqeusts

这种请求使用 OPTIONS 方法,发送下列头部

  • Origin:与简单的请求相同。
  • Access-Control-Request-Method:请求自身使用的方法。
  • Access-Control-Request-Headers:(可选)自定义的头部信息,多个头部以逗号分隔。以下是一个带有自定义头部 NCZ 的使用 POST 方法发送的请求。

例如:

  • Origin: http://www.nczonline.net
  • Access-Control-Request-Method: POST
  • Access-Control-Request-Headers: NCZ
发送这个请求后,服务器可以决定是否允许这种类型的请求。服务器通过在响应中发送如下头部与
浏览器进行沟通。
  • Access-Control-Allow-Origin:与简单的请求相同。
  • Access-Control-Allow-Methods:允许的方法,多个方法以逗号分隔。
  • Access-Control-Allow-Headers:允许的头部,多个头部以逗号分隔。
  • Access-Control-Max-Age:应该将这个 Preflight 请求缓存多长时间(以秒表示)。

例如:

  • Access-Control-Allow-Origin: http://www.nczonline.net
  • Access-Control-Allow-Methods: POST, GET
  • Access-Control-Allow-Headers: NCZ
  • Access-Control-Max-Age: 1728000

带凭据的请求

默认情况下,跨源请求不提供凭据( cookie HTTP 认证及客户端 SSL 证明 等 )。 通 过 将
withCredentials 属性设置为 true ,可以指定某个请求应该发送凭据。如果服务器接受带凭据的请
求,会用下面的 HTTP 头部来响应。

Access-Control-Allow-Credentials: true

 跨浏览器的CORS

即使浏览器对 CORS 的支持程度并不都一样,但所有浏览器都支持简单的(非 Preflight 和不带凭据的)请求,因此有必要实现一个跨浏览器的方案。检测 XHR 是否支持 CORS 的最简单方式,就是检查是否存在 withCredentials 属性。再结合检测 XDomainRequest 对象是否存在,就可以兼顾所有浏览器了。
function createCORSRequest(method, url){ 
 var xhr = new XMLHttpRequest(); 
 if ("withCredentials" in xhr){ 
 xhr.open(method, url, true); 
 } else if (typeof XDomainRequest != "undefined"){ 
 vxhr = new XDomainRequest(); 
 xhr.open(method, url);
} else { 
 xhr = null; 
 } 
 return xhr; 
} 
var request = createCORSRequest("get", "http://www.somewhere-else.com/page/"); 
if (request){ 
 request.onload = function(){ 
 //对 request.responseText 进行处理
 }; 
 request.send(); 
}
  • abort():用于停止正在进行的请求。
  • onerror:用于替代 onreadystatechange 检测错误。
  • onload:用于替代 onreadystatechange 检测成功。
  • responseText:用于取得响应内容。
  • send():用于发送请求。
以上成员都包含在 createCORSRequest() 函数返回的对象中,在所有浏览器中都能正常使用。

其他跨域技术

图像Ping

上述第一种跨域请求技术是使用 <img>标签。动态创建图像经常用于 图像 Ping 。图像 Ping 是与服务器进行简单、单向的跨域通信的一种方式。
var img = new Image(); 
img.onload = img.onerror = function(){ 
 alert("Done!"); 
}; 
img.src = "http://www.example.com/test?name=Nicholas";
图像 Ping 常用于跟踪用户点击页面或动态广告曝光次数。图像 Ping 有两个主要的缺点,一是只
能发送 GET 请求,二是无法访问服务器的响应文本。因此,图像 Ping 只能用于浏览器与服务器间的单向通信。

JSONP

JSONP 由两部分组成:回调函数和数据。
回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的JSON 数据。
http://freegeoip.net/json/?callback=handleResponse
JSONP 是通过动态 <script> 元素 来使用的,使用时可以为src 属性指定一个跨域 URL
function handleResponse(response){ 
 alert("You’re at IP address " + response.ip + ", which is in " + 
 response.city + ", " + response.region_name); 
} 
var script = document.createElement("script"); 
script.src = "http://freegeoip.net/json/?callback=handleResponse"; 
document.body.insertBefore(script, document.body.firstChild);

Comet

Comet Alex Russell 发明的一个词儿,指的是一种更高级的 Ajax 技术(经常也有人称为“服务器推送”)。Ajax 是一种从页面向服务器请求数据的技术,而 Comet 则是一种服务器向页面推送数据的技术。Comet 能够让信息近乎实时地被推送到页面上,非常适合处理体育比赛的分数和股票报价。
有两种实现 Comet 的方式: 长轮询
轮询
无论是短轮询还是长轮询,浏览器都要在接收数据之前,先发起对服务器的连接。两者最大的区别
在于服务器如何发送数据。短轮询是服务器立即发送响应,无论数据是否有效,而长轮询是等待发送响应。轮询的优势是所有浏览器都支持,因为使用 XHR 对象和 setTimeout() 就能实现。
HTTP
它在页面的整个生命周期内只使用一个 HTTP 连接。具体来说,就是浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性地向浏览器发送数据。
使 用 XHR 对象实现 HTTP 流的典型代码如下所示。
function createStreamingClient(url, progress, finished){ 
 var xhr = new XMLHttpRequest(), 
 received = 0; 
 xhr.open("get", url, true); 
 xhr.onreadystatechange = function(){ 
 var result; 
 if (xhr.readyState == 3){ 
 //只取得最新数据并调整计数器
 result = xhr.responseText.substring(received); 
 received += result.length; 
 //调用 progress 回调函数
 progress(result); 
 } else if (xhr.readyState == 4){ 
 finished(xhr.responseText); 
 } 
 }; 
 xhr.send(null); 
 return xhr; 
} 
var client = createStreamingClient("streaming.php", function(data){ 
 alert("Received: " + data); 
 }, function(data){ 
 alert("Done!"); 
 });
这个 createStreamingClient() 函数接收三个参数:要连接的 URL 、在接收到数据时调用的函
数以及关闭连接时调用的函数。

Web Sockets

Web Sockets 的目标是在一个单独的持久连接上提供全双工、双向通信。
1. Web Sockets API
要创建 Web Socket ,先实例一个 WebSocket 对象并传入要连接的 URL
var socket = new WebSocket("ws://www.example.com/server.php");
实例化了 WebSocket 对象后,浏览器就会马上尝试创建连接。与 XHR 类似, WebSocket 也有一
个表示当前状态的 readyState 属性。不过,这个属性的值与 XHR 并不相同,而是如下所示。
  • WebSocket.OPENING (0):正在建立连接。
  • WebSocket.OPEN (1):已经建立连接。
  • WebSocket.CLOSING (2):正在关闭连接。
  • WebSocket.CLOSE (3):已经关闭连接。
2. 发送和接收数据
Web Socket 打开之后,就可以通过连接发送和接收数据。要向服务器发送数据,使用 send() 方法
并传入任意字符串。
var socket = new WebSocket("ws://www.example.com/server.php"); 
socket.send("Hello world!");
因为 Web Sockets 只能通过连接发送纯文本数据,所以对于复杂的数据结构,在通过连接发送之前,必须进行序列化。下面的例子展示了先将数据序列化为一个 JSON 字符串,然后再发送到服务器:
var message = { 
 time: new Date(), 
 text: "Hello world!", 
 clientId: "asdfp8734rew" 
}; 
socket.send(JSON.stringify(message));
当服务器向客户端发来消息时, WebSocket 对象就会触发 message 事件。这个 message 事件与
其他传递消息的协议类似,也是把返回的数据保存在 event.data 属性中。
socket.onmessage = function(event){ 
 var data = event.data; 
 //处理数据
};
3. 其他事件
WebSocket 对象还有其他三个事件,在连接生命周期的不同阶段触发。
  • open:在成功建立连接时触发。
  • error:在发生错误时触发,连接不能持续。
  • close:在连接关闭时触发。
var socket = new WebSocket("ws://www.example.com/server.php"); 
socket.onopen = function(){ 
 alert("Connection established."); 
}; 
socket.onerror = function(){ 
 alert("Connection error."); 
}; 
socket.onclose = function(){ 
 alert("Connection closed."); 
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值