在XMLHttpRequest
未标准化之前,受限于同源策略,XMLHttpRequest
不能进行跨站请求,因而开发者想出了〈使用 JSONP 跨站请求〉中的方式。
在 XMLHttpRequest Level 1 的规范中,XMLHttpRequest
可以进行跨站请求,至于是否能接受响应,要看伺服端是否支持〈CORS 协议〉,以最简单的GET
为例,伺服端若在响应标头中,包含了Access-Control-Allow-Origin
,而值是发出请求的来源网站,那么XMLHttpRequest
就可以接受响应。
例如,若在我的网站上有个 PHP 如下:
<?php
header('Access-Control-Allow-Origin: http://output.jsbin.com');
header('Content-Type: application/json');
switch($_GET['id']) {
case '1':
$result = '{"name":"Justin","age":35}';
break;
case '2':
$result = '{"name":"momor","age":32}';
break;
case '3':
$result = '{"name":"Hamimi","age":3}';
break;
default:
$result = '{"name":"NOBODY","age":0}';
}
echo $result;
?>
其中Access-Control-Allow-Origin
为http://output.jsbin.com
,因此当底下的网页是处于该网域下时:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<body>
ID:<input id="id">
<button id="test">JSONP 测试</button>
<span id="result"></span>
</body>
<script type="text/javascript">
document.getElementById('test').onclick = function() {
let id = document.getElementById('id').value;
let request = new XMLHttpRequest();
request.onload = function(evt) {
let req = evt.target;
if(req.status === 200) {
let person = req.response;
document.getElementById('result').innerHTML
= `${person.name}, ${person.age}`;
}
};
request.responseType = 'json';
request.open('GET',
`https://openhome.cc/Gossip/ECMAScript/samples/CORS-1.php?id=${id}`);
request.send(null);
};
</script>
</body>
</html>
按我观看执行结果。
那么XMLHttpRequest
就可以直接获取响应,而程序流程不用任何改变,上面的范例可以输入 ID(1、2、3 是有数据的)来获取我网站上提供的数据。
跨域请求实际上会发出,只是伺服端若没有支持跨域的响应标头,浏览器就不会读取响应,然而,因为伺服端确实收到了请求,也就有可能在非预期的情况下,改变了伺服端的状态。
为了避免这类问题,在某些情况下,像是设定了某些请求标头、使用了PUT
、DELETE
等方法(参考〈What requests use CORS?〉,浏览器在发出跨域请求前会用OPTION
做预请求(preflight request),确定服务器真的接受跨域请求,才真的发出程序中指定的请求。
在〈Server-Side Access Control (CORS)〉中提供有一些 CORS 协议的范例可供参考。