AJAX即Asynchronous JavaScript and XML,意思就是用JS异步网络请求,也就是通过回调函数获得数据。AJAX请求数据主要依靠XMLHttpRequest对象, 而低版本的IE依靠的是ActiveXObject对象:
function success(text) {
var textarea = document.getElementById('test-response-text');
textarea.value = text;
}
function fail(code) {
var textarea = document.getElementById('test-response-text');
textarea.value = 'Error code: ' + code;
}
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
request = new ActiveXObject('Microsoft.XMLHTTP');
}
request.onreadystatechange = function () { // 状态发生变化时,函数被回调
if (request.readyState === 4) { // 成功完成
// 判断响应结果:
if (request.status === 200) {
// 成功,通过responseText拿到响应的文本:
return success(request.responseText);
} else {
// 失败,根据响应码判断失败原因:
return fail(request.status);
}
} else {
// HTTP请求还在继续...
}
}
// 发送请求:
request.open('GET', '/api/categories');
request.send();
通过检测window对象是否有XMLHttpRequest属性来确定浏览器是否支持标准的XMLHttpRequest,不要根据浏览器的navigator.userAgent来检测浏览器是否支持某个JS特性,一是因为这个字符串本身可以伪造,二是通过IE版本判断JS特性没有确定性。
XMLHttpRequest对象的open()方法有3个参数,第一个参数指定请求方式,第二个参数指定URL地址,第三个参数指定同步还是异步,默认是true,千万不要设为false,这里一般不写。最后调用send()方法发送请求,GET请求不需要参数,POST请求需要把body部分以字符串或者FormData对象传进去。
JSONP
AJAX请求指定的URL必须和当前页面完全一致,这是因为浏览器的同源策略规定的,就是域名、协议、端口号都要要相同。
那么如何用JS请求外域(就是其他网站)的URL呢?方法有以下几种:
- 通过Flash插件发送HTTP请求,这种方式可以绕过浏览器的安全限制,但必须安装Flash,而且Flash用起来麻烦,所以现在用得也越来越少了。
- 通过在同源域名下架设一个代理服务器来转发,JS负责把请求发送到代理服务器:
/proxy?url=http://www.sina.com.cn
,代理服务器再把结果返回,这样就遵守了浏览器的同源策略,但这种方式需要额外的服务器做开发。 - 利用浏览器允许跨域引用JS资源即JSONP,但它限制只能用GET请求,并且要求返回JS。JSONP通常以函数调用的形式返回,先准备好调用的函数,然后给页面动态加一个<script>节点,相当于动态读取外域的JS资源。
以163的股票查询URL为例,对于URL:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice
refreshPrice({"0000001":{"code": "0000001", ... });
// 回调函数:
function refreshPrice(data) {
var p = document.getElementById('test-jsonp');
p.innerHTML = '当前价格:' +
data['0000001'].name +': ' +
data['0000001'].price + ';' +
data['1399001'].name + ': ' +
data['1399001'].price;
}
function getPrice() {
var js = document.createElement('script'),
head = document.getElementsByTagName('head')[0];
js.src = 'http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice';
head.appendChild(js);
}
CORS
HTML5推出了新的跨域策略CORS,全称Cross-Origin Resource Sharing:Origin表示浏览器当前页面域,当JS向外域发起请求浏览器收到响应后,首先检查Access-Control-Allow-Origin是否包含本域,如果包含则此次跨域请求成功,如果不包含则请求失败。
假设本域是my.com,外域是sina.com,只要响应头Access-Control-Allow-Origin为http://my.com
或者是*,本次请求就可以成功。可见,跨域能否成功取决于对方服务器是否愿意给你设置一个正确的Access-Control-Allow-Origin,决定权始终在对方手中。这种跨域请求称之为“简单请求”,它包括GET、HEAD和POST(POST的Content-Type类型仅限application/x-www-form-urlencoded、multipart/form-data和text/plain),并且不能出现任何自定义头(例如,X-Custom: 12345),通常能满足基本需求。
目前主流的浏览器都支持HTML5,在引用外域资源时都要验证CORS。例如引用第三方字体文件:
/* CSS */
@font-face {
font-family: 'FontAwesome';
src: url('http://cdn.com/fonts/fontawesome.ttf') format('truetype');
}
// 如果该CDN服务商未正确设置Access-Control-Allow-Origin,那么浏览器无法加载字体资源。
对于PUT、DELETE以及其他类型如application/json的POST请求,在发送AJAX请求之前,浏览器会先发送一个OPTIONS请求(称为preflighted请求)到这个URL上,询问目标服务器是否接受:
OPTIONS /path/to/resource HTTP/1.1
Host: bar.com
Origin: http://my.com
Access-Control-Request-Method: POST
服务器必须响应并明确指出允许的Method:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://my.com
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
Access-Control-Max-Age: 86400
浏览器确认服务器响应的Access-Control-Allow-Methods头确实包含将要发送的AJAX请求的Method,才会继续发送AJAX,否则,抛出一个错误。
由于以POST、PUT方式传送JSON格式的数据在REST中很常见,所以要跨域正确处理POST和PUT请求,服务器端必须正确响应OPTIONS请求。