在 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 協議的範例可供參考。