• 跨域方法:JSONP、iframe


    同源策略:浏览器出于安全考虑,会限制文档或脚本中发起的跨域请求(但src请求不受此限)资源的加载。实际上通过抓包软件可以发现请求和响应都会成功,但是响应数据并不会被浏览器加载。不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不可以使用 XMLHttpRequest 对象和Fetch发起读写其他web服务器的资源,主要有如下限制:

    1.) Cookie、LocalStorage 和 IndexDB 无法读取 2.) DOM 和 Js对象无法读写 3.) AJAX 请求不能发送

    同源不是指脚本的来源,而是指脚本所嵌入的文档来源。具体要求域名,协议和端口都相同。

    比如需要加载另一个域的一张图片资源,通过<img src='xxx'>静态引用的方式可以成功;但是通过ajax方法会报错:No 'Access-Control-Allow-Origin' header is present on the requested resource.

    如果只是希望用post提交数据,不需要处理响应数据,可以:

      a.用form的submit(跨域)提交最简单,此时会导致当前页面跳转到别的域。可以将form的target设置到一个隐藏的iframe解决。

      b.用ajax的post方法,但是不能监听到响应,而且会报错。

    跨域请求数据的方法:

    JSONP:最简单,支持度高,但只能发送get请求,传递的参数大小有限。用js动态创建一个script标签,其中src=’www.targetweb.com?callback=foo’,然后定义foo(data)方法,最后将script标签append到页面。sript标签会自动向目标网站发送请求,服务器将数据作为参数同函数名一起返回(foo(jsonData)函数调用表达式)。实际上是返回了<script>foo(xxxx)</script>脚本文件,所以函数foo会立即调用。

    //JSONP异常处理
    var ele = document.createElement('script');
    ele.type = "text/javascript";
    ele.src = 'www.targetweb.com?callback=foo';
    //异常时的回调
    ele.onerror = function() {
        console.log('error');
    };
    //js成功加载(下载并执行完之后)后的回调
    ele.onload = function() {
        console.log('loaded');
    };
    document.body.appendChild(ele);

    相当于添加了如下script标签:

    <script>
        function foo(jsonpData){
        //...
        }
    </script>
    <script src=’www.targetweb.com?callback=foo&param1=paramSubmitByUrl’></script>
    <!--后台返回的js文件实际上就是函数调用:foo(jsonpData) -->

    jQuery中的跨域请求

    $.ajax({
        type : "get",
        async:true,
        url : "/taotao-rest/category",    //跨域的请求地址
        crossDomain:true,
        dataType : "jsonp",
        jsonp: "callbackName",//(不必要)定义请求参数名(相当于表单的name),供后台获取参数使用,默认为"&callback=?"中的callback
        jsonpCallback:"showLists",//(不必要)自定义jsonp回调函数名称,默认为jQuery自动生成的随机函数名替代上述?号;在本例中后台应该返回showLists(jsonData)
        success : function(json){
            //为什么不定义jsonpCallback也不会报错?因为jQuery内部会自定义jsonpCallback(若无),并用responseContainer接受后台返回的数据再存入json变量中。
            //window[ callbackName ] = function() {
            //    responseContainer = arguments;
            //};
            //最后jQuery之后会将callBackName和对应的script标签删除
            //所以此时才可以直接用json数据
        },  
        beforeSend: function(){
        //jsonp方式时此方法不会触发,因为不是用 XHR发送的请求
        },
        error: function(xhr){
        //jsonp 方式时此方法不会触发
        }
    });

    H5的WebSocket:使用ws和wss作为协议,允许跨域通信,可以相互推送信息。

    iframe方式
    因为jsonp只能发送get请求,所以对于跨域post提交大量数据时无能为力,此时可以用iframe跨域提交post请求。(兼容性好,兼容ie7/8/9)

    <iframe>元素表示嵌套的浏览上下文,可以有效地将另一个HTML页面嵌入到当前页面中。实践中常将form表单的target设置为iframe的name属性,这样submit提交后服务器返回的页面就会在iframe中显示,主页面不会再刷新。iframe常用属性:

    name属性
    嵌入的iframe的名称,该名称可以用作<a>标签的target属性值,在指定的框架中打开被链接文档;也可用作<form>标签的target属性值,规定在何处打开action URL。或<input>/<button>标签的formtaget属性值,规定表示提交表单后在哪里显示接收到响应的名称或关键词,formtarget 属性会覆盖 <form> 元素的 target 属性。

    src
    规定在 iframe 中显示的文档的 URL。

    与iframe的交互:
      在主页面获取iframe内的文档对象:document.getElementById('childiframe').contentDocument.xxx
      在iframe中的脚本访问主页面的window的属性:window.parent.document
      在主页面访问iframe中window里的属性:document.getElementById('childiframe').contentWindow
    注意:如果iframe和主页面不在同一个域,则不能通过js互相访问(window或DOM),被浏览器限制了。把两个子页面的document.domain都指向主域则可以互相访问。 

    iframe的弊端
    window 的 onload 事件需要在iframe 的所有资源加载完毕才会触发,另外会占用主页面的可用连接数量。所以一般尽量少用iframe,要用的话最好等主页面加载完后再设置iframe的src加载内容。

    使用iframe跨域

    主要是利用Window.name属性传递数据。每个iframe都有window对象,其name属性用于标记window的名字,可以存贮2M大小的数据。除非主动修改或关闭,同一个window打开任意页面其值都不变。window.name和iframe.name是2个概念,初始化时window.name会默认取当前iframe的name的值,但是之后就无关联。修改window.name不影响iframe.name。只有在DOM对象下可以修改iframe的name属性。另外在设置a元素和form表单的target属性时,window.name优先于iframe.name。

    步骤:

    1.使用post提交数据

      使用主页面的form跨域提交post数据,target指定到隐藏的iframe;或者在隐藏的iframe中用ajax提交post数据;

    2.服务器端返回跨域下的任意空文档,script部分需要设置window.name=data,data为服务器响应的数据。

    3.将iframe的src设置为主页面的域,否则无法访问iframe的window。之后再从iframe的contentWindow.name中取数据。

    //主页面
    var isloaded=false;    //防止循环刷新iframe
    //chrome和ie下onload事件的触发时机不一样?!!!
    document.getElementById('iframe0').onload=function (e) {
        if(!isloaded){
            isloaded=true;
            e.target.src='null.html';//设主页面同域的任意页面,响应数据在脚本中
        }else{
          //使用跨域获取的数据
          console.log(document.getElementById('iframe0').contentWindow.name);
        }
    }

    CORS(Cross-origin resource sharing)跨域资源共享
    如果只需要兼容IE11,则可以使用CORS。服务器实现CORS接口(在header配置Access-Control-Allow-origin属性),浏览器不会丢弃响应,就可以跨源通信了。

    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://example.com/', true);
    xhr.withCredentials = true;

    websocket协议跨域

    以上皆为个人理解,如有错误之处,欢迎留言指正。
  • 相关阅读:
    使用Eclipse 创建Spring Boot项目
    springMVC中文乱码问题
    Java POI Excel 导入导出
    springMVC + quartz实现定时器(任务调度器)
    spring配置Converter、Formatter日期转换器
    springMVC+springJDBC+Msql注解模式
    基于JavaScript封装的Ajax工具类
    H5音乐播放器
    JavaWeb+MySql分页封装
    JS如何判断是否为ie浏览器的方法(包括IE10、IE11在内)
  • 原文地址:https://www.cnblogs.com/kevin2chen/p/6482461.html
Copyright © 2020-2023  润新知