跨站请求 CORS


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-Originhttp://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 是有数据的)来获取我网站上提供的数据。

跨域请求实际上会发出,只是伺服端若没有支持跨域的响应标头,浏览器就不会读取响应,然而,因为伺服端确实收到了请求,也就有可能在非预期的情况下,改变了伺服端的状态。

为了避免这类问题,在某些情况下,像是设定了某些请求标头、使用了PUTDELETE等方法(参考〈What requests use CORS?〉,浏览器在发出跨域请求前会用OPTION做预请求(preflight request),确定服务器真的接受跨域请求,才真的发出程序中指定的请求。

在〈Server-Side Access Control (CORS)〉中提供有一些 CORS 协议的范例可供参考。


展开阅读全文