ajax 的全称是Asynchronous JavaScript and XML(异步的JavaScript和XML),即用JavaScript去异步的获取XML文件作为交换格式,由 Jesse James Garrett 在2005年提出。其中,Asynchronous 是异步的意思,它有别于传统web开发中采用的同步的方式。
关于同步和异步
首先常用的是:普通B/S模式代表同步,AJAX技术代表异步
同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕
(参考:同步与异步的概念)
Ajax是一种无需刷新页面就能够从服务器获取数据的技术,会带来更好的用户体验。
Ajax并非一种新的技术,而是几种原有技术的结合体。它由下列技术组合而成。
- 使用CSS和HTML来实现页面,表达信息。
- 使用XMLHttpRequest来和web服务器进行数据的异步交换。
- 使用javascript来操作DOM,实现动态局部刷新。
ajax原理和XmlHttpRequest对象
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。
Ajax技术的核心是XHR(XMLHttpRequest对象)。
XMLHttpRequest是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。
XHR为向服务器发送请求和解析服务器响应提供了流畅的接口,能够以异步方式从服务器取得更多信息,当用户单击后,可以不必刷新页面也能取得新数据。可以使用XHR对象取得新数据,然后再通过DOM将新数据插入到页面中。
Ajax通信与数据格式无关,这种技术就是无需刷新页面即可从服务器取得数据,但不一定是XML数据。
XmlHttpRequest对象
使用Ajax技术,首先要创建xhr对象。1
2var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
//所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)
XMLHttpRequest这个对象的属性有:
属性 | 描述 |
---|---|
onreadystatechange | 每次状态改变所触发事件的事件处理程序。 |
responseText | 从服务器进程返回数据的字符串形式。 |
responseXML | 从服务器进程返回的DOM兼容的文档数据对象。 |
status | HTTP响应状态,从服务器返回的数字代码,比如常见的404(未找到)和200(已就绪) |
status Text | 伴随状态码的字符串信息 |
readyState | 对象状态值 |
readyState对象状态值:
readyState | 含义 |
---|---|
0 (未初始化) | 对象已建立,但是尚未初始化(尚未调用open方法) |
1 (初始化) | 对象已建立,尚未调用send方法 |
2 (发送数据) | send方法已调用,但是当前的状态及http头未知 |
3 (数据传送中) | 已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误 |
4 (完成) | 数据接收完毕,此时可以通过通过responseXml和responseText获取完整的回应数据 |
Status:HTTP响应状态码,常见的有:
使用XMLHttpRequest对象向服务器发送请求
XMLHttpRequest这个对象向服务器发送请求的方法有:open()和send().
open方法:开启一个请求以备发送,但它不会向服务器端发起正式请求。
xhr.open(method,url[,async = true]);
open方法指定了:- 向服务器发送请求的方式,即post还是get。
- 请求的url地址和传递的参数。
- 传输方式,false为同步,true为异步。默认为true,一般省略不填写。如果是异步通信方式(true),客户机就不等待服务器的响应;如果是同步方式(false),客户机就要等到服务器返回消息后才去执行其他操作。我们需要根据实际需要来指定同步方式,在某些页面中,可能会发出多个请求,甚至是有组织有计划有队形大规模的高强度的request,而后一个是会覆盖前一个的,这个时候当然要指定同步方式。
Send方法用来发送请求。
xhr.send([data = null]);
如果使用POST请求,send()里需要有内容。向服务器发送GET请求:用于向服务器查询某些信息。
1
2xhr.open("get","example.php?name1=value1&name2=value2",true);
xhr.send(null);向服务器发送POST请求:用于向服务器发送需要保存的数据
1
2
3
4
5xhr.open('post','example.php',true);
//使用setRequestHeader()来添加HTTP头
xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
//在send()方法中添加需要发送的数据。
xhr.send('name1=value1&name2=value2');请求参数序列化
对于get请求,查询字符串中每个参数的名称和值必须使用encodeURIComponent()进行编码,然后才能放到URL的末尾,所有的名值对必须用&分割;
对于post请求,发送的数据也必须使用encodeURIComponent()进行编码。
因此,需要对这些参数/数据进行序列化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//参数序列化
function serialize(data){
if(!data) return '';
var pairs = [];
for(var name in data){
if(!data.hasOwnProperty(name)) continue;
if(typeof data[name] === 'function') continue;
var value = data[name].toString();
name = encodeURIComponent(name);
value = encodeURIComponent(value);
pairs.push(name + '=' + value);
}
return pairs.join('&');
}
//GET请求
var ulr = 'example.json?' + serialize(formdata);
xhr.open('get',url,true);
xhr.send(null);
//post请求
xhr.open('post','example.json',true);
xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
xhr.send(serialize(formdata));
使用XMLHttpRequest对象向取得响应:
- responseText:获得字符串形式的响应数据
- responseXML:获得XML形式的响应数据
- status和statusText:以数字和文本形式返回HTTP状态码
- getAllResponseHeader():获取所有的响应报头
- getResponseHeader():查询响应中的某个字段的值
- readyState:表示请求/响应过程的当前活动阶段,其属性值包含0-4变化(参考上文)
- readystatechange:只要readyState属性的值由一个值变成另一个值,都会触发一次readystatechange事件。可以利用此事件来检测每次状态变化后readyState的值。
一个完整的AJAX案例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//创建xhr对象。所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
//处理返回数据。当请求被发送到服务器时,我们需要执行一些基于响应的任务。每当 readyState 改变时,就会触发 onreadystatechange 事件。
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
//http 状态码 200到300是指服务端正常返回;304是告诉客户端取缓存数据
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
callback(xhr.responseText);
} else {
alert("请求错误:" + xhr.status + " " + xhr.statusText);
}
}
};
//发送请求
xhr.open('post', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(null);
XMLHttpRequest是完全用来向服务器发出一个请求的,它的作用也局限于此,但它的作用是整个ajax实现的关键,因为ajax无非是两个过程,发出请求和响应请求。并且它完全是一种客户端的技术。而XMLHttpRequest正是处理了服务器端和客户端通信的问题所以才会如此的重要。
Ajax封装函数
以下代码是将上述的过程进行的封装,使得使用时只要调用函数并在回调函数中实现功能就可以了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 <script>
/*
* method是请求方式
* url是网络请求的地址
* postboidy是post方式请求时的提交数据
* successCallback是请求成功的函数
* errorCallback是请求失败的函数
*/
function request (method,url,postbody,successCallback,errorCallback) {
//创建一个请求对象
if(window.XMLHttpRequest){
var request = new XMLHttpRequest();
}else{
var request = new ActiveXObject("Microsoft.XMLHttp");
}
if(arguments[0]=="POST"){
//创建请求
request.open(method,url,true);
//设置上传类型
request.setRequestHeader("content-type","application/x-www-form-urlencoded");
}else if(arguments[0]=="GET"){
request.open(method,url,true);
}
//发送请求
request.send(postbody);
//状态监听
request.onreadystatechange = function () {
if(request.readyState ==4 && request.status == 200){
//请求成功的回调函数
if(successCallback){
successCallback(request.responseText);
}
} else if (request.readyState == 4 && request.status != 200) {
//请求失败的回调函数
if(errorCallback){
errorCallback(request.statusText);
}
}
}
}
</script>
使用方法举例:
下面定义了一个简单的表单提交用户名和密码,使用回调函数拿到后台返回的JSON串后,转换成对象再取出其中的信息,告知用户是登陆成功还是失败1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49//html部分
//调用封装好的函数
<script src="request.js"></script>
<div class="form">
<input type="text" id="username">
<input type="password" id="passwd">
<button id="loginBtn">登录</button>
</div>
//js部分
<script>
var username = document.getElementById("username");
var passwd = document.getElementById("passwd");
var loginBtn = document.getElementById("loginBtn")
loginBtn.onclick = function () {
//设置请求地址及提交内容
var url = "login.php";
var postbody = "username="+username.value+"&passwd="+passwd.value;
//发起ajax请求,并使用回调函数实现功能
request("POST",url,postbody,function (resText) {
//把json格式的字符串转换成对象
var obj = JSON.parse(resText);
alert(obj.msg);
});
}
</script>
//php部分login.php
<?php
//从请求中获取用户名和密码
$username = $_POST["username"];
$passwd = $_POST["passwd"];
//连接服务器
@$mysqli = new mysqli("localhost","root","","user");
$mysqli->query("set names utf8");
//查询语句
$sql = "SELECT * FROM user WHERE username='$username' AND passwd='$passwd'";
//数据库执行查询
$result = $mysqli->query($sql);
//判断查询结果是否有值,并定义返回字符串
//echo出来的就是返回前端的数据
if($result->num_rows > 0){
echo '{"errorcode":0,"msg":"登陆成功"}';
}else{
echo '{"errorcode":1,"msg":"用户名或密码错误"}';
}
//关闭服务器
$mysqli->close();
?>
参考链接:http://www.jianshu.com/p/ff9e1139ea51
XMLHttpRequest 2级
XMLHttpRequest 1级只是把已有的XHR对象的实现细节描述了出来。而XMLHttpRequest 2级则进一步发展了XHR。
2 级 XMLHttpRequest 引入了大量的新功能(例如跨源请求、上传进度事件以及对上传/下载二进制数据的支持等),一举封杀了我们网络应用中的疯狂黑客。这使得 AJAX 可以与很多尖端的 HTML5 API 结合使用,例如 File System API、Web Audio API 和 WebGL。
xhr.overrideMimeType()方法
overrideMimeType()用于重写XHR响应的MIME类型。返回响应的MIME类型决定了XHR对象如何处理它。
现在,让我们利用 XMLHttpRequest 新增的 responseType 和 response 属性,告知浏览器我们希望返回什么格式的数据。
xhr.responseType
在发送请求前,根据您的数据需要,将 xhr.responseType 设置为“text”、“arraybuffer”、“blob”或“document”。请注意,设置(或忽略)xhr.responseType = ‘’ 会默认将响应设为“text”。
xhr.response
成功发送请求后,xhr 的响应属性会包含 DOMString、ArrayBuffer、Blob 或 Document 形式(具体取决于 responseTyp 的设置)的请求数据。
凭借这个优秀的新属性,我们可以修改上一个示例:以 ArrayBuffer 而非字符串的形式抓取图片。将缓冲区移交给 BlobBuilder API 可创建 Blob:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (this.status == 200) {
var bb = new BlobBuilder();
bb.append(this.response); // Note: not xhr.responseText
var blob = bb.getBlob('image/png');
...
}
};
xhr.send();
ArrayBuffer 响应
ArrayBuffer 是二进制数据通用的固定长度容器。如果您需要原始数据的通用缓冲区,ArrayBuffer 就非常好用,但是它真正强大的功能是让您使用 JavaScript 类型数组创建底层数据的“视图”。实际上,可以通过单个 ArrayBuffer 来源创建多个视图。例如,您可以创建一个 8 位整数数组,与来自相同数据的现有 32 位整数数组共享同一个 ArrayBuffer。底层数据保持不变,我们只是创建其不同的表示方法。
例如,下面以 ArrayBuffer 的形式抓取我们相同的图片,但是现在,会通过该数据缓冲区创建无符号的 8 位整数数组。1
2
3
4
5
6
7
8
9
10
11var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
var uInt8Array = new Uint8Array(this.response); // this.response == uInt8Array.buffer
// var byte3 = uInt8Array[4]; // byte at offset 4
...
};
xhr.send();
Blob 响应
如果您要直接处理 Blob 且/或不需要操作任何文件的字节,可使用 xhr.responseType=’blob’:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21window.URL = window.URL || window.webkitURL; // Take care of vendor prefixes.
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.response;
var img = document.createElement('img');
img.onload = function(e) {
window.URL.revokeObjectURL(img.src); // Clean up after yourself.
};
img.src = window.URL.createObjectURL(blob);
document.body.appendChild(img);
...
}
};
xhr.send();
Blob 可用于很多场合,包括保存到 indexedDB、写入 HTML5 文件系统 或创建 Blob 网址(如本例中所示)。
参考资源:XMLHttpRequest2 新技巧 http://www.html5rocks.com/zh/tutorials/file/xhr2/
HTTP请求
一个完整的HTTP请求过程,通常有下面7个步骤:
- 建立TCP连接
- Web浏览器向Web服务器发送请求命令
- Web浏览器发送请求头信息
- Web服务器应答
- Web服务器发送应答头信息
- Web服务器向浏览器发送信息
- Web服务器关闭TCP连接
一个HTTP请求一般由4部分组成:
- HTTP请求的方法或动作,比如GET/POST请求
- GET:向服务器请求信息,一般用于信息获取,使用URL传递参数,对所发送信息的数量有限制,一般在2000个字符以内
- POST:向服务器发送信息,一般用于修改服务器上的资源。对所发送信息的数量无限制
- 正在请求的URL
- 请求头,包含一些客户端环境信息、身份验证信息等
- 请求体(请求正文),包含客户提交的查询字符串信息,表单信息等。
一个HTTP响应一般由3部分组成:
- 一个数字和文字组成的状态码,用来显示请求是成功还是失败
- 响应头,和请求头一样,包含许多有用的信息,例如服务器类型、日期时间、内容类型和长度等。
- 响应体:也就是响应正文。
HTTP状态码:
HTTP状态码由3位数字构成,其中首位数字定义了状态码的类型:
- 1XX:信息类,表示收到Web浏览器请求,正在进一步处理中。
- 2XX:成功,表示用户请求被正确接收、理解和处理,比如 200 OK。
- 3XX:重定向,表示请求没有成功,客户必须采取进一步的动作
- 4XX:客户端错误,表示客户端提交的请求有错误,例如404 Not Found,意味着请求中所引用的文档不存在。
- 5XX:服务器错误,表示服务器不能完成对请求的处理,如500错误。
Ajax原理解读:
把服务器端看成一个数据接口,它返回的是一个纯文本流,当然,这个文本流可以是XML格式,可以是Html,可以是Javascript代码,也可以只是一个字符串。这时候,XMLHttpRequest向服务器端请求这个页面,服务器端将文本的结果写入页面,这和普通的web开发流程是一样的,不同的是,客户端在异步获取这个结果后,不是直接显示在页面,而是先由javascript来处理,然后再显示在页面。
Ajax优缺点
Ajax的优点
- 页面无刷新,在页面内与服务器通信,给用户的体验非常好。
- 使用异步方式与服务器通信,不需要打断用户的操作,具有更加迅速的响应能力。
- 可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
- 基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
Ajax的缺点
- ajax干掉了back按钮,即对浏览器后退机制的破坏。后退按钮是一个标准的web站点的重要功能,但是它没法和js进行很好的合作。这是ajax所带来的一个比较严重的问题,因为用户往往是希望能够通过后退来取消前一次操作的。
- 安全问题:ajax技术就如同对企业数据建立了一个直接通道。这使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。ajax的逻辑可以对客户端的安全扫描技术隐藏起来,允许黑客从远端服务器上建立新的攻击。还有ajax也难以避免一些已知的安全弱点,诸如跨站点脚步攻击、SQL注入攻击和基于credentials的安全漏洞等。
- 对搜索引擎的支持比较弱
- 破坏了程序的异常机制
- 其他方面的一些问题:如违背了url和资源定位的初衷。例如,我给你一个url地址,如果采用了ajax技术,也许你在该url地址下面看到的和我在这个url地址下看到的内容是不同的。这个和资源定位的初衷是相背离的。
AJAX跨域处理
同源策略
两个页面拥有相同的协议(Protocol)、端口(Port)、和主机(host)那么这两个页面就是属于同一个源(Origin)。
跨域资源访问
- 当协议、端口和主机中任意一个不相同时,都算作不同源(域)
-不满足同源策略的资源访问,叫跨域资源访问
比如:http://www.xifengxx.com 和 http://www.baidu.com 这两者之间相互请求资源,是跨域资源访问。
-W3C 定义了 CORS。
-现代浏览器已经实现了对 CORS 的支持。
Javascript出于安全方面的考虑,不允许跨域调用其他页面的对象。
www.abc.com/index.html调用www.abc.com/service.php (非跨域)
www.abc.com/index.html调用www.efg.com/service.php (跨域)
www.abc.com/index.html调用bbs.abc.com/service.php (跨域)
www.abc.com/index.html调用www.abc.com:81/service.php(跨域)
www.abc.com/index.html调用https://www.abc.com/service.php(跨域)
CORS标准原理(Cross-Origin Resource Sharing)
- 浏览器捕获到a.b.com应用往x.y.com的服务器发起的请求
- 浏览器检查请求情况确定是否需要先做一次预请求来验证x.y.com的服务器是否允许发当前请求过去,如果需要发预请求则浏览器发起一个OPTIONS的请求到x.y.com的服务器验证继续第3步,否则直接发送请求到x.y.com服务器继续第5步
- 服务器根据浏览器发过来的header信息,然后根据服务器端对资源的配置返回资源的实际控制权限配置
- 浏览器验证预请求返回的信息,判断是否可以将请求发送到x.y.com的服务器,如果不行则异常退出,否则继续第5步
- 浏览器发送实际请求至x.y.com服务器
- 服务器返回请求数据及资源控制配置信息至浏览器
- 浏览器验证资源控制信息是否允许当前实际请求的取到数据,如果不允许则异常退出,否则继续第8步
- 浏览器返回x.y.com返回的数据至a.b.com的应用
CORS(跨源资源共享)定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
如何实现?
在服务器端做一些小小的改造,添加如下代码:
header(‘Access-Control-Allow-Origin:*’);
header(‘Access-Control-Allow-Methods:POST,GET’);
其他跨域技术
- Frame 代理
-JSONP
-Comet
-Web Sockets
Frame代理
此模式主要参照CORS规范原理,通过在目标服务器放置一个代理文件,然后通过该代理文件来与服务器端进行数据交互,返回数据通过消息通讯返回给上层应用来实现跨域的数据交互。
此方式也支持通过代理文件配置资源可访问的来源。
- 当a.b.com的应用要往x.y.com的服务器取数据时,首先会用iframe载入预先放置在x.y.com服务器上的代理文件
- 服务器端返回做了配置的代理文件
- 代理文件载入完成后a.b.com的应用将要发送的请求指令通过消息通信方式传递给代理文件
- 代理文件验证a.b.com是否在预先配置的白名单中,如果不在则异常返回,否则直接发送请求至x.y.com服务器
- 服务器返回数据至代理文件
- 代理文件通过消息通讯机制将请求结果返回给a.b.com的应用
优点:
-参照 CORS 标准
-支持各种请求方法 GET POST PUT DELETE
缺点:
-需要在目标服务器放置代理文件
-由于首次发起请求时需要载入代理文件,在载入代理文件之前的所有请求都会存在一定的延时。
-对于低版本浏览器受限于消息通讯机制的限制,对于并发量大的请求返回时可能存在较大延时。
JSONP
JSON with Padding(填充式 JSON): 用于解决主流浏览器的跨域数据访问的问题。
利用<script>
可以跨域,请求一段 JavaScript 代码,然后执行 JavaScript 代码来实现跨域。
JSONP支持get请求方式,不支持post请求方式。
举例:
在www.aaa.com页面中:1
2
3
4
5
6<script>
function jsonp(json){
alert(json["name"]);
}
</script>
<script src="http://www.bbb.com/jsonp.js"></script>
在www.bbb.com页面中:jsonp({'name':'洪七','age':24});
DEMO示例
来源:慕课网-Ajax全接触
分析:
通过Ajax实现动态无刷新页面进行查询/添加操作。
涉及技术点:
- HTML+CSS简单布局
- Ajax + JSON 实现页面查询/添加操作。
- jQuery基础运用
- Ajax跨域解决方案(JSONP + CORS)
DEMO下载:下载地址
参考资源:
- NEJ的前后端通讯系统:
https://github.com/genify/nej/blob/master/doc/AJAX.md (☆☆☆☆) - JavaScript跨域与解决方法:
http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html(☆☆写的不错,但看不懂) - 跨域基本知识:
http://www.cnblogs.com/scottckt/archive/2011/11/12/2246531.html(☆☆☆☆) - JavaScript函数节流:
http://www.alloyteam.com/2012/11/javascript-throttle/(☆☆☆☆) - Ajax基础知识讲解:
http://www.nowamagic.net/librarys/veda/detail/750 (☆☆☆☆) - Ajax使用学习:http://www.jianshu.com/p/ff9e1139ea51 (☆☆☆☆☆)