AJAX

只愿长相守 提交于 2020-03-07 07:01:12

目录

1. XMLHttpRequest

2. 使用 XMLHttpRequest 时的基本处理流程

3. 通过XMLHttpRequest 进行同步通信

4. 超时处理

5. 响应

6. 跨源限制

7. 跨源通信

JSONP

iframe 攻击(iframe hack)

window.postMessage

XMLHttpRequest Level 2


AJAX 是 Asynchronous JavaScript + XML 的简称 。AJAX 一词的实际含义为“不发生页面跳转、异 步载入内容并改写页面内容的技术”。在实际操作中,AJAX 不仅仅会使用XML 数据,很多时候也会对 JSON 或纯文本进行操作。AJAX 的关键在于它是以异步的方式执行的。异步处理的优点是不会让用户白白等待。对于同步处 理来说,在处理完来自服务器的响应之前,用户无法进行任何其他操作,只能等待。如果服务器的响应 发生了延迟,会让用户误以为页面失去了响应。在优先考虑用户体验时,与同步处理相比,采用异步处 理的方式更为合适,这一点是显而易见的。

1. XMLHttpRequest

通过使用XMLHttpRequest 对象令JavaScript 动态地向服务器发送请求。

XMLHttpRequest 的跨浏览器支持

if (!window.XMLHttpRequest) { 
    // Internet Explorer 6 
    XMLHttpRequest = function () {  
        var objs = ['MSXML2.XMLHTTP.6.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];  
        for (var i = 0; i < objs.length; i++) {   
            var obj = objs[i];   
            try {    
                return new ActiveXObject(obj);   
            } catch (ignore) {   
            }  
        }  
        throw new Error('Cannot create XMLHttpRequest object.'); 
    } 
} 
var xhr = new XMLHttpRequest();

2. 使用 XMLHttpRequest 时的基本处理流程

对服务器的 URL 进行指定,以发送请求。

发送请求

var xhr = new XMLHttpRequest(); 
xhr.onreadystatechange = function() { 
    if (xhr.readyState == 4) {  
        if (xhr.status == 200) {   
            alert(xhr.responseText);  
        } 
    } 
}; 
xhr.open('GET', 'http://example.com/something'); 
xhr.setRequestHeader('If-Modiified-Since', 'Thu 01 Jun 1970 00:00:00 GMT'); 
xhr.send(null); 

onreadystatechange 这一事件处理程序将会在XMLHttpRequest 对象的状态发生变化时被调用。

readyState 的含义
readyState 含义
0 open() 尚未被调用
1 send() 尚未被调用
2 服务器尚未返回响应
3 正在接收来自服务器的响应
4 完成了对来自服务器的响应的接收

在 status 中包含了响应的状态码。如果通信正常结束,该值为200。

传递给 open() 的参数是 HTTP 请求类型及通信目标服务器的 URL。如果传递了 false 值至第 3 个参数,XMLHttpRequest 就会执行同步通信。该参数的默认值为 true,也 就是将执行异步通信。第 4 个参数与第5 个参数分别是用户ID 和密码。在向需要进行 身份认证的服务器发送请求时要用到这两个参数。

setRequestHeader() 用于请求头部的设置。在通信的目标服务器中会自动发送对应的Cookie, 所以并不需要显式地设置。

实际向服务器发送请求的是send()。如果是POST 请求类型,则会将参数所收到的数据发送至服务 器。如果是 GET 请求类型或 HEAD 请求类型等不需要发送数据的 HTTP 请求类型,则参数为 null。

3. 通过XMLHttpRequest 进行同步通信

如果要执行同步通信,则不必 对 onreadystatechange 事件处理程序进行设定。在执行了send() 之后该处理将会进入待机状态,只要在 send() 之后继续书写对响应的处理操作即可。

通过 XMLHttpRequest 进行同步通信

var xhr = new XMLHttpRequest(); 
xhr.open('GET', 'http://example.com/something', false); 
// 将第 3 个参数指定为 false 的话就会执行同步通信 
xhr.setRequestHeader('If-Modified-Since', 'Thu, 01 Jan 1970 00:00:00 GMT'); xhr.send(null);  // 此时,客户端侧的处理将会进入待机状态 
// 执行至此时已经完成了对响应的接收 
if (xhr.status === 200) { 
    alert(xhr.responseText); 
} 

4. 超时处理

如果要取消请求,其实只需要执行abort() 方法即可。而通过setTimeout() 方法可以在一定时间后执 行 abort() 方法,从而实现超时功能。另外还有一个 clearTimeout()方法,如果在一定时间内收到了返回的 响应,该方法就将会取消 abort() 的执行。

var xhr = new XMLHttpRequest(); 
var timerId = window.setTimeout(function() { 
    xhr.abort(); 
}, 5000);  // 5 秒后将会超时

xhr.onreadystatechange = function() { 
    if (request.readyState === 4) {  
        // 取消超时处理  
        window.clearTimeout(timeId); 
    } 
};

5. 响应

通用类型的响应 :可以通过responseText 属性来引用一个XMLHttpRequest 响应。

XML 类型的响应 :以XML 的形式接收XMLHttpRequest 的响应。

JSON 形式的响应 :返回JSON 的 API。

//通用类型的响应
var xhr = XMLHttpRequest(); 
// ... 
var dom = document.getElementById('foo'); 
foo.innerHTML = xhr.responseText;

//XML 类型的响应
var xhr = XMLHttpRequest(); 
// ... 
var xml = xhr.responseXML;
// 假定 xml 的内部是这样的内容 
// <result> 
//  <apiversion>1.0</apiversion> 
//  <value>foo</value> 
//</result> 
alert(xml.getElementsByTagName('value')[0].firstChild.nodeValue); // => foo

//JSON 类型的响应
var xhr = XMLHttpRequest(); 
// ... 
var json = JSON.parse(xhr.responseText);
// 假定 json 的内部是这样的内容 
// { 
//    "apiversion": 1.0, 
//    "value": "foo" 
// } 
alert(json.value); // => foo 

6. 跨源限制

跨源限制指的是,对源不同的通信进行限制。而这里的源指的是由URL 的协议(http: 或 https: 等)、主机名、端口号所构成的元素。在Web 领域,为了确保安全性,只有同源的通信才能被允许进行, 这称为同源策略。

对于XMLHttpRequest 来说,同源策略的含义是,一个XMLHttpRequest 对象只能发送至一个特定的 服务器,即提供了使用该XMLHttpRequest 对象的文档的下载的那个服务器。不过,只要让服务器转发 该请求,就能够将请求发送至不同域的服务器。

7. 跨源通信

可以通过服务器转发或Flash 来实现跨源请求的发 送,也可以通过 JavaScript 实现跨源通信。

JSONP

JSONP 是 JSON with Padding 的简称。这里的 Padding 指的是向 JSON 数据中添加函数名。JSONP 是无法在POST 请求类型中使用。

JSONP 的使用示例

<script> 
function foo(json) { 
    // 使用 json 数据进行一些操作
}
function loadData() { 
    var elem = document.createElement('script'); 
    // 将 foo 指定为所要执行的回调函数 
    // 在使用 JSONP 的 API 中,常常可以对 callback 函数的名称进行指定 
    elem.src = 'http://api.example.com/some-data&callback=foo'; 
    // 将 script 标签添加至 head 中 
    // 这时 DOM 将被重建,并载入 script 标签的 src 的内容 
    // 载入之后就会执行 foo 函数 
    document.getElementByTagName('head')[0].append(elem); 
} 
</script> 

iframe 攻击(iframe hack)

API 请求

首先,在my.example.com 的页面中将会指定一个other.example.com 中的html 来创建一个iframe。这里 的关键点在于 URL 中包含了哈希片段。哈希片段被指定为了进行 API 调用时所需的数据。

响应

在 other.example.com 的页面中,将会通过使用XMLHttpRequest 之类的方式调用other.example. com 中的功能并获取数据。这时还没有进行跨源的功能调用,必须在取得数据后再将它们传回之前 的 my.example.com 中的页面。为此,需要在other.example.com 的页面内创建一个iframe,并将其指向 my.example.com 中的页面。即孙iframe。这一孙iframe 的 URL 也要被指定为哈希片段。和在父页面中创 建的指向 other.example.com的子 iframe一样,数据将被置于哈希片段之中。

回调函数

由于孙iframe 与父页面都是my.example.com 中的页面,因此可以在孙iframe 中执行父页面中的函 数。这时,孙iframe 的 onload 将会调用父页面的函数,从而实现callback 的调用。当然了,在调用函数 时所使用的参数是从子iframe 传来的location.hash 的值。至于子iframe 将会调用哪一个页面,则是在创 建子iframe 时通过哈希片段来指定的。只要在创建时将所指定的这一页面置于子iframe 内即可。如此一 来,从父页面的角度来看,就实现了与跨源通信相同的执行结果。

通过 iframe 实现跨源通信(父页面)

<html> 
    <head>  
        <title> 父页面 </title>  
        <script>  
        // 在跨源通信中用于获取数据的函数  
        function getData() {   
            // 此处子 iframe 的 URL 为 other.example.com 的页面   
            // 参数则是在 # 之后的数据   
            frames[0].location.href =
                'http://other.example.com/api.html#' +    
                '{' +     
                    // 这里是事实上希望执行的 API     
                    '"api": "http://other.example.com/some-data",' +     
                    // 在子 iframe 中指定的孙 iframe 的 URL     
                    '"callback": "http://my.example.com/callback.html"' +    
                '}';  
        }

        // 在跨源通信中作为回调函数被执行的函数  
        // 由孙 iframe 调用  
        function callback(param) {   
            document.getElementById("result").innerHTML = param; 
            frames[0].frames[0].location.href = 'dummy.gif';  
        }  
        </script> 
    </head> 
    <body>  
        <input type="button" value=“从 other.example.com 获取数据” οnclick="getData()">
        <div id="result"></div>  
        <iframe id="child-frame" src="dummy.gif" style="display: none;"></iframe>
    </body> 
</html>

通过 iframe 实现跨源通信(子 iframe)

<html> 
    <head>  
        <title> 子 iframe</title>  
        <script>  
        function executeApi() {   
            // 将 location.hash 的第一个字符 (#) 去除,将剩余部分以 JSON 格式进行分析   
            var param = JSON.parse(location.hash.substring(1));   
            var xhr = new XHMHttpRequest();    
            xhr.onreadystatechange = function() {    
                if (xhr.readyState == 4 && xhr.status == 200) {     
                    var iframe = document.getElementById('grandchild-iframe');
                    iframe.location.href = param.callback + '#' + xhr.responseText;    
                }   
            };   
            xhr.open(param.api, 'GET');   
            xhr.send(null);  
        }  
        </script> 
    </head> 
    <body οnlοad='executeApi()'>  
        <iframe id="grandchild-iframe" src="dummy.gif" style="display: none;"></iframe> 
    </body> 
</html>

通过 iframe 实现跨源通信(孙 iframe)

<html> 
    <head>  
        <title> 孙 iframe</title>  
        <script>  
        window.onload = function() {   
            window.top.callback(location.hash);  
        }  
        </script> 
    </head> 
    <body></body> 
</html>

window.postMessage

通过 postMessage 实现跨源通信(父页面)

<html> 
    <head>  
        <title> 父页面 </title>  
        <script>   
            // 在跨源通信中用于获取数据的函数   
            function getData() {    
                // 对子 iframe 进行 postMessage 操作  
                frames[0].postMessage('http://other.example.com/some-data','http://other.example.com');   
            }   
            // 在跨源通信中作为回调函数被执行的函数   
            // 被设定为用于接收来自子 iframe 的消息   
            window.addEventListener('message', function(event) {    
                if (event.origin !== 'http://other.example.com') {     
                    return;    
                }    
                // 将结果保存于 event.data 中
                document.getElementById("result").innerHTML = event.data;   
            }, false);   
        </script>  
    </head>  
    <body>   
        <input type="button" value=“从 other.example.com 获取数据” οnclick="getData()">   
        <div id="result"></div>   
        // 将 other.example.com 的页面指定为子 iframe 的 URL   
        <iframe id="child-frame" src="http://other.example.com/api.html" style="display: none;"></iframe> 
    </body> 
</html>

通过 postMessage 实现跨源通信(子 iframe)

<html> 
    <head>  
        <title> 子 iframe</title>  
        <script>  
        window.addEventListener('message', function(event) {   
            if (event.origin !== 'my.example.com') {    
                return;   
            }   
            var xhr = new XHMHttpRequest();   
            xhr.onreadystatechange = function() {    
                if (xhr.readyState == 4 && xhr.status == 200) {     
                    // 将 responseText 作为消息返回 
                    event.source.postMessage(xhr.responseText, 'http://my.example.com');    
                }   
            };   
            var url = event.data;   
            xhr.open(url, 'GET');   
            xhr.send(null);  }, false);  
        </script> 
    </head> 
    <body></body> 
</html>

XMLHttpRequest Level 2

XMLHttpRequest Level 2

var xhr = new XMLHttpRequest(); 
xhr.open('GET', 'http://other.example.com', true); 
xhr.withCredentials = true; // 设定为将会发送 Cookie 
xhr.onreadystatechange = function() { 
    // 进行一些操作 
}; 
xhr.send();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!