一个iOS程序员眼中的跨域问题

作者: 杭城小刘 | 来源:发表于2017-03-29 08:56 被阅读1279次

    摘要: 跨域问题是web开发领域一个常见的问题,相信每个web开发者都遇到“跨域”的问题
    最近公司的iOS开发任务比较少,所以自己最近开始了Web开发的任务,在用H5做了很多页面,所做的东西和之前客户端界面一模一样,只是做好h5搬到微信上。本人开发环境:html开发ide一会用HBulder一会儿用Sublime。

    235815_vhrw_1778933.png

    为了模拟真实的环境,所以HBulder新建了WebApp,跑起来可以针我的大玫瑰金上运行看效果(iphone6s plus玫瑰金)。我很傻的以为手机上运行没错就没问题了,不会存在传说中的<b>跨域问题</b>,没想到周五下午要合并代码到Dev分支,才发现合并进去页面报错。

    235833_q0iv_1778933.png

    只能怪自己"too young too naive",回头一想,原来App请求接口类似于C/S不存在跨域问题。而网页这种方式只要协议、域名、端口、有一个不一致就会导致跨域,由于自己所做的页面有40多,所以改起来烦的要死,问同事除了将数据放进url后来形成类似http://xxx/com?s=xx&q=yy这种形式有木有其他办法?最后找出一种快速修改的方法,见下面代码

    修改前

    $.ajax({
    type: "post",
    url: early_children_url ,
    data:urlJson,
    success: function(data) {
    console.log(storageData);
    swal({
    //省略...
    

    修改后

    var neonataldetail = '{' +
    '"babyinfoid":"' + earlyBabyInfoId + '",' +
    '"days":"' + day + '",' +
    '"feedingtimes":"' + feedingtimes + '",' +
    '"feedingway":"' + feedingway + '",' +
    '"stoolfrequency":"' + stoolfrequency + '",' +
    '"urinate":"' + urinate + '",' +
    '"yellowish":"' + yellowish + '",' +
    '"weight":"' + weight + '"' +
    '}';
    
    var dataJson = {
    "type": early_children_perinatal_type,
    "neonataldetail": JSON.parse(neonataldetail)
    };
    var urlJson = JSON.stringify(dataJson);
    $.ajax({
    dataType: "jsonp",
    type: "post",
    url: early_children_url + "?s=" + urlJson,
    success: function(data) {
    console.log(storageData);
    swal({
    title: "提交成功",
    text: "",
    showCancelButton: false,
    closeOnConfirm: false,
    showLoaderOnConfirm: true
    }, function(data) {
    window.location.href = "early-children.html";
    });
    },
    error: function() {
    swal("上传失败,请检查网络后重试");
    }
    });
     
    

    最后查了相关的资料发现跨域问题大体有3种解决方案:(1)、代理;(2)、JSONP(支持get请求);(3)、XHR2;(4)、CORS跨域资源共享;(5)、通过修改document.domain来解决iframe跨域问题;(6)、window.name进行跨域;(7)、HTML5的window.postMessage方法

    具体说明:基于浏览器的安全考虑,由于同源策略的限制,不同域名、不同端口、不同协议的对象不能互相调用。(其实浏览器成功发送请求并拿回了数据 只是浏览器的同源策略 禁止了获取 )

    代理:通过统一域名下的Web服务器创建一个代理。

    举例说明:www.hangzhou.com的a.html需要调用www.shanghai.com下的b.php服务,显然这违反了同源策略,所以就需要通过代码这个方法去解决问题。具体怎么做?可以在www.hangzhou.com下可以写个exchange.php。将去请求www.shanghai.com下的服务,之后将结果返回给a.html。这样访问www.hangzhou.com/exchange.php的效果等于访问www.shanghai.com的服务

    JSONP:JSON with padding。是JSON的一种使用方式,适用于主流浏览器的跨域访问问题。JSONP由2部分组成:回调函数和数据

    //www.hangzhou.com
    
    <script type="text/javascript">
    
       function getArg(data){
    
          //处理数据
    
       }
    
    </script>
    

    <script type="text/javascript" src="http://www.shanghai.com/showUser.php?callback=getArg."></script>

    js文件载入成功后会执行我们在url参数中指定的函数,所以JSONP是需要服务端进行配合的。

    //www.shanghai.com
    
    <?php
    
         $callback = $_GET['callback'];//得到回调函数名
    
         $data = array('a','b','c');//要返回的数据
    
         echo $callback.'('.json_encode($data).')';//输出
    
    ?>
    

    XHR2:HTML5提供的XMLHttpRequest Level2已经实现了跨域访问以及其他的一些新功能。但不适配所有浏览,比如IE10以下的版本均不支持。

    此外还需要在服务端修改

    header('Access-Controller-Allow-Origin:*');
    
    header('Access-Controller-Allow-Methods:POST、GET');
    
    window.onload = function () {
    
       var xhr = new XMLHttpRequest();
    
       //判断浏览器是否支持XHR2
    
       if (xhr.withCredentials === undefined) return false;
    
    
    
       xhr.open("get", "http://www.baidu.com");
    
       xhr.onreadystatechange = function () {
    
      if (xhr.readyState !== 4) return;//忽略未完成的调用
    
        if (xhr.status === 200) {
    
           console.log(xhr.responseText);
    
        }
    
      }
    
      xhr.send(null);
    
    }
    

    CORS跨域资源共享:Cross-Origin Resource Sharing定义了进行访问跨域资源时浏览器如何与服务器通信。

    <script type="">
    
       var xhr = new XMLHTTPRequest();
    
       xhr.open("get","/showUser.php");
    
       xhr.send();
    
    </script>
    

    如果使用CORS

    <script type="">
    
       var xhr = new XMLHTTPRequest();
    
       xhr.open("get","http://hangzhou.com/showUser.php");
    
       xhr.send();
    
    </script>
    

    代码与之前的代码差别就是使用了绝对路径。服务端对于CORS的支持在于Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。

    CORS和JSONP对比

    1、JSONP只可以实现get请求,CORS可以所有类型的HTTP请求

    2、CORS开发者可以使用普通的XMLHTTPRequest请求和获取数据

    3、JSONP支持较老的浏览器,而老版本的浏览器不支持CORS

    通过修改document.domain来跨域

    浏览器的同源策略限制之一就是不能通过Ajax去请求不同源的文档,限制之二就是浏览器中不同域的框架之间是不能进行js交互。

    不同框架之间可以获取window对象,但是window对象的属性和方法不可获取到。

    http://www.hangzhou.com/a.html的一个frame的src是http://www.xihu.hangzhou.com/b.html这时候document.domain就可以上场了,

    不过需要注意,document.domain的设定有限制,必须设置成自身或比自身更高一级的父域,且主域必须相同。

    <iframe id = "iframe" src="http://xihu.hangzhou.com/b.html" onload="showUser()"></iframe>
    
    <script type="text/javascript">
    
    document.domain = 'hangzhou.com';//设置成主域
    
    function showUser(){
    
    alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
    
    }
    
    </script>
    在页面http://xihu.hangzhou.com/b.html也需要设置
    
    <script type="text/javascript">
    
    document.domain = 'hangzhou.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
    
    </script>
    

    通过window.name来跨域

    window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的

    HTML5的window.postMessage方法跨域

    window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

    传送门:https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policyhttp://www.ruanyifeng.com/blog/2016/04/cors.html

    相关文章

      网友评论

      • LD_左岸:不存在跨域 那么我们后台让我每次接口请求都传递cookie 给他 然后他才能拿到具体的某个用户 如果我不传cookie 那么关于用户的信息就拿不到

        大神觉的 我不传cookie 别的方式能拿到吗
        LD_左岸:@左岸__ session会有一个过期时间限制
        所以第一:在每次程序启动的函数didFinishLaunchingWithOptions里在走一遍请求登录的接口 用来更新session
        第二:每次发所有接口的请求前判断下session是否过期 过期了提示 然后跳转登录
        LD_左岸:@杭城小刘 只要实现这个原理都可以,换个方法传递,比如说将信息保存到 LocalStorage 和 SessionStorage 里面,然后顺着接口传递给后台
        有这么一个疑问 就是 我如果用AFN请求的话 他是默认帮我存储这个cookie的对吧 之后我在请求首页 或者别的接口的数据时 AFN发起的请求头里 会自动携带这个session 但是session会有一个过期时间限制 所以在每次程序启动的函数didFinishLaunchingWithOptions里在走一遍请求登录的接口 用来更新session 然后 每次发所有接口的请求前判断下session是否过期 过期了提示 然后跳转登录

        这么做可不可以?这么做相当于我没直接接触session cookie

        我想像中的方式是 登录成功之后 返回个userID 然后每次我传递这个useid来判定具体用户 会更直接了当 我感觉我们后台整这个session cookie的整的我很不明白
        杭城小刘:@左岸__ HTTP是无状态的协议,也就是说用户与服务器握手之后建立一个连接了,之后服务器不记得你干了什么事情,这时候就需要session保持用户都干了什么(比如用户成功登录了购物网站,之后他点击加入商品A,然后基于推荐系统,可能将商品B加入到了购物车,最后在结算的时候服务器不知道你干了什么,对于这种情况就需要 session 了,服务端将 session 有关的信息返回给客户端,客户端保存 session 的一些信息到 cookie中)。所以只要实现这个原理都可以,换个方法传递,比如说将信息保存到 LocalStorage 和 SessionStorage 里面,然后顺着接口传递给后台,当然这是最 low 的做法。最好还是研究下 cookie ,用 cookie 传递过去
      • LD_左岸:我现在后头的接口返回的是jsonp
        而不是json
        我用AFN请求Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
        大神之见 我该怎么拿到数据.......:pray:
        LD_左岸:@杭城小刘 奥 我这么写好像可以了
        NSURLSession *session = [NSURLSession sharedSession];
        NSURL *url = [NSURL URLWithString:@""];
        [[session dataTaskWithURL:url
        completionHandler:^(NSData *data,
        NSURLResponse *response,
        NSError *error) {
        // handle response
        if (data) {
        NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"stringJSONed: %@",jsonString);

        //Do something with the received jsonString, just like in @ rmaddy's reply
        } else {
        NSLog(@"Error loading data: %@", error);
        }
        }] resume];
        return;
        杭城小刘:@左岸__ 跨域只针对web端开发才存在,跨域是浏览器同源策略造成的,客户端不存在跨域问题,应该是你解析数据出错了

      本文标题:一个iOS程序员眼中的跨域问题

      本文链接:https://www.haomeiwen.com/subject/neqeottx.html