zoukankan      html  css  js  c++  java
  • JavaScript跨域资源请求(CORS)解决方案

    跨域:当协议、主域名、子域名、端口号中任意一个不相同时都不算同一个域,而在不同域之间请求数据即为跨域请求。解决方法有以下几种(如有错误欢迎指出)以请求图片url为例:

    1.通过XMLHttpRequest对象实现(IE10以下不支持)

    XMLHttpRequest2.0已经实现了对CORS的原生支持,只需要在访问资源的时候使用绝对URL即可,需要在服务器端将头信息“Access-Control-Origin"设为”*“或当前域名的地址:

    html&javascript:

     1     <button id="btn">load</button>
     2     <div id="process"></div>
     3     <div id="img"></div>
     4 
     5     <script>
     6         var btn = document.getElementById('btn'),
     7             pro = document.getElementById('process'),
     8             pic = document.getElementById('img');
     9 
    10         btn.onclick = function() {

    12 var xhr = new XMLHttpRequest(); 17 xhr.onreadystatechange = function() { 18

    19 if(xhr.readyState == 4 && xhr.status == 200){ 20
    21 var img = document.createElement('img'); 22 img.src = JSON.parse(xhr.responseText).src; 23 pic.appendChild(img); 24 } 25 } 26 xhr.open('GET','http://127.0.0.1:8085/AJAX/ajax/server.php',true); 27 xhr.send(null); 28 } 29 </script>

    这个例子是在本地测试的,html文件所在的位置是localhost:8085下,虽然127.0.0.1也是指的localhost,但他们也不是同一个域,可以利用这两个地址来模拟跨域请求。

    php:

    <?php
    header("Content-Type:text/plain");
    header("Access-Control-Allow-Origin:http://localhost:8085");//设置头部,不设置的话请求会被拒绝
    echo '{"src":"http://www.pinkbluecp.cn/face_alignment/img/picture.jpg"}';
    ?>

    post请求也差不多:

    btn.onclick = function() {
                if(typeof XMLHttpRequest != 'undefined'){
                    var xhr = new XMLHttpRequest();
                }else if(typeof ActiveXObject != 'undefined'){
                    var xhr = new ActiveXObject("Microsoft.XMLHTTP");
                }
    
                xhr.onreadystatechange = function() {
                    console.log(1)
                    if(xhr.readyState == 4 && xhr.status == 200){
                        console.log(2)
                        pic.innerHTML = xhr.responseText;
                    }
                }
                xhr.open('POST','http://127.0.0.1:8085/AJAX/ajax/server.php',true);
                xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");    //需要设置下头部
                var str = 'active=login';
                xhr.send(str);

    php

    <?php
    header("Content-Type:text/plain");
    header("Access-Control-Allow-Origin:http://localhost:8085");
    
    if($_POST['active'] == 'login'){
        echo 'success';
    }else {
        echo "error";
    }
    ?>

    发送数据需要设置一下头部的Content-Type字段。

    2.IE(8-10)中实现CORS:

    IE8中引入了XDomainRequest对象,该对象和XHR相似,但它能实现安全可靠的跨域通信,不过在IE11中已经无法使用了。

     1 var btn = document.getElementById('btn'),
     2             pro = document.getElementById('process'),
     3             pic = document.getElementById('img');
     4 
     5         btn.onclick = function() {
     6             var xhr = new XDomainRequest();
     7             xhr.onload = function() {
     8                 var img = document.createElement('img');
     9                 console.log(xhr.responseText)
    10                 // img.src = JSON.parse(xhr.responseText).src;
    11                 img.src = xhr.responseText;
    12                 console.log(img.src);
    13                 pic.appendChild(img);
    14             };
    15             xhr.onerror = function() {  //XDR无法确定响应状态,需要添加onerror来检测错误
    16                 alert("Error!");
    17             }
    18             xhr.open('GET','http://127.0.0.1:8085/AJAX/ajax/server.php'); //该对象的的请求都是异步,没有第三个参数
    19             xhr.send(null);
    20         }        

    XDR与XHR不同之处在于:

    a. cookie不会随请求发送,也不会随响应返回

    b. 只能设置请求头中的Content-Type字段

    c. 不能访问响应头部信息

    d. 只支持GET和POST请求

    两者的方法也略有不同,XDR的open方法只有异步一种状态,所以只要传两个参数method和url,在请求返回之后会触发load事件,但无法获取响应状态码,所以想判断请求是否成功只能添加error事件。它也可以和XHR一样添加超时。

    关于XDR的post请求,虽然js高编上有介绍,但是在多次尝试后发现已经行不通了,高编上说XDR有专门的属性contentType来设置请求头信息,但在浏览其中会报错:

    无法进行设置contentType的操做,后来查阅了一下这篇博客,貌似XDR已经无法在设置请求头了,详情可以去看原博。

    3. 实现跨浏览器的CORS

    js高编上有一段实现跨浏览器的CORS实现函数:

        <button id="btn">load</button>
        <div id="process"></div>
        <div id="img"></div>
    
        <script>
            var btn = document.getElementById('btn'),
                pic = document.getElementById('img');
    
            btn.onclick = function() {
                console.log(1);
                var xhr = createCORS('GET','http://127.0.0.1:8085/AJAX/ajax/server.php');
                if(xhr){
                    console.log(2)
                    xhr.onload = function(){
                        console.log(3)
                        var img = document.createElement('img');
                        img.src = xhr.responseText;
                        pic.appendChild(img);
                    };
                    xhr.onerror = function() {
                        alert("Error");
                    };
                    xhr.send(null);
                }
            }
    
            function createCORS(method,url){  //参考js高编
                console.log('fun')
                var xhr = new XMLHttpRequest();
                if('withCredentials' in xhr){    //检测是否含有凭据属性
                    xhr.open(method,url,true);
                }else if(typeof XDomainRequest != 'undefined'){    //兼容ie
                    xhr = new XDomainRequest();
                    xhr.open(method,url);
                }else {
                    xhr = null;
                }
                return xhr;
            }
        </script>

    除了IE10-外所有浏览器都有withCredentials属性,所以可以根据这个属性来判断是否支持XMLHttpRequest2.0.

    4.图片Ping

    图片Ping就是利用图片的src可以使用跨域资源的特性来实现,但是只能实现简单的单向请求,在img的src中传入一个其他域的url并附带一些参数。

    5.JSONP

    JSONP和图片Ping很相似,也是利用script标签中的链接不受同源限制可以向不同域传递信息,但它传递的是一个已经存在的回调函数,服务器接收到后,将响应数据传入该回调函数并执行该函数从而达到获取数据的目的。

    先来看下<script>标签中传入src得到的是什么:

    <script src="test.txt"></script>

    在src中传入一个文本文件

    浏览器中报语法错误,hello world这个变量未定义,服务器响应的数据就时test.txt的内容,而script把这段纯文本当作js代码来解析,当在test.txt中将这段文字加上单引号后:

    浏览器不报错了,因为把它当作字符串来解析了。现在把链接换成一个php文件:

    php

    <?php
    echo "Hello World!";
    ?>

    结果一样,浏览器同样也会报错,加上引号后同样也不报错,如果这时服务器端返回的是一个函数名,而该页面正好又有一个同名函数会怎样:

    php:

    <?php
    echo "callback('hello world')";
    ?>

    html

    <body>
        <script>
            function callback(res){
                alert(res);
            }
        </script>
        <script src="jsonp.php"></script>
    </body>

    结果:

     callback函数被执行了,因为后台的响应数据被script标签当作js代码执行了,所以这就能理解jsonp的原理了,利用这个回调函数可以获得后台传来的数据:

    html:

    <body>
        <script>
            function jsonp(res){
                for(var i in res){
                    console.log("key:"+i+";value:"+res[i]);
                }
            }
        </script>
        <script src="jsonp.php?callback=jsonp"></script>  //可动态创建
    </body>

    php:

    <?php
    $str = '{"name":"Lee","age":20}';
    $callback = $_GET['callback'];
    echo $callback."($str)";
    ?>

    上面一个简单的例子展示了jsonp如何用回调函数获取服务器响应的json数据:

     JSONP的有点在于可以访问到服务器的响应文本,不过缺点就是要从其他域加载代码执行,必须要保证其他域的安全性,不然可能响应信息中会附带恶意脚本,还有一点就是无法确定请求是否失败,即使失败也不会有提示。

    6.iframe跨域

    iframe跨域与jsonp相似,也利用了src不受同源限制的特性。当A域下的x.html页面要访问B域下的y.html中的信息可以通过A域下的z.html页面作为代理来获取信息:

    x.html: 

     1 <!doctype html>
     2 <html>
     3 <head>
     4 <meta charset="utf-8">
     5 <title>无标题文档</title>
     6 </head>
     7 
     8 <body>
     9 <iframe src="http://127.0.0.1:8085/AJAX/ajax/proxya.html" style="display: none"></iframe>
    10 <p id="getText"></p>
    11 <script>
    12     function callback(text){
    13         text = JSON.parse(decodeURI(text));
    14         document.getElementById("getText").innerHTML= '姓名:' + text.name + '; 年龄:' + text.age;    
    15     }
    16 </script>
    17 </body>
    18 </html>

    y.html:

     1 <!doctype html>
     2 <html>
     3 <head>
     4 <meta charset="utf-8">
     5 <title>无标题文档</title>
     6 </head>
     7 
     8 <body>
     9 <iframe id="myfarme" src="###"></iframe>
    10 <script>
    11     window.onload = function(){
    12         var text = '{"name":"cheng","age":22}';  //存储一个json格式的数据
    13         document.getElementById('myfarme').src="http://localhost:8085/AJAX/ajax/proxyb.html?content="+encodeURI(text); //将数据传个代理页面处理,此时src中的地址不受同源限制,与jsonp相似
    14     }
    15 </script>
    16 </body>
    17 </html>

    z.html:

     1 <!doctype html>
     2 <html>
     3 <head>
     4 <meta charset="utf-8">
     5 <title>无标题文档</title>
     6 <script>
     7 window.onload = function(){
     8     var text = window.location.href.split('=')[1];  //通过分解url获取y.html传来的参数
    9 top.callback(text);  //调用x.html的callback函数 10 } 11 </script> 12 </head> 13 14 <body> 15 </body> 16 </html>
  • 相关阅读:
    labVIEW学习——属性节点
    MATLAB学习
    WinForm中键盘按键问题
    TextBox的智能感知
    使用脚本操作UpdatePanel中控件的问题
    DBNull.Value,null,String.Empty之间的区别和联系
    Hotmail如何对已经存在的邮件地址进行更改?
    第一个hibernate项目
    html静态实现左边导航右边显示链接页面
    Struts2学习
  • 原文地址:https://www.cnblogs.com/cjw-ryh/p/7674038.html
Copyright © 2011-2022 走看看