zoukankan      html  css  js  c++  java
  • 跨域请求解决方案

    本文转载自:http://www.cnblogs.com/xiaoheimiaoer/p/4101588.html 
    在前端开发过程中,难免和服务端产生数据交互。一般情况我们的请求分为这么几种情况:
    1. 只关注发送,不关注接收
    2. 不仅要发送,还要关注服务端返回的信息
      • 同域请求
      • 跨域请求
        上面提到了一个概念,我们这里简单做一下讲解。什么叫做跨域?一般情况下,跨域分为三种情况:跨协议、跨子域、跨域名。下面距离梳理一下这三种情况。
    • 跨协议:比如说我现在的域名地址是http://www.12306.cn,有一些请求需要发送到https://www.12306.cn,此时这个请求相对与http://www.12306.cn来说就是跨协议的请求
    • 跨子域:比如说我现在是http://www.12306.cn,在登录的时候需要请求http://passport.12306.cn这个接口,此时这个请求就是跨子域的请求
    • 跨域名:我们都知道现在的12306添加新的常用联系人的时候需要验证身份证号码,据说校验身份证号和姓名是否匹配只有公安部才有这个接口,并且使用费用相当高(扯远了),此时需要请求公安部的服务接口,此时从12306发出的请求就需要跨域名了
    我们需要知道的是,跨域请求只要满足这是三种情况之一就会被认定为跨域请求。
     
        目前流行比较广的跨域请求解决方案有:window.name、document.domain、服务端代理、jsonp、前端代理。
     
        以下介绍的方式只是实现原理,没有过多考虑安全性,可以根据自己的情况进行选择。
     
    • window.name
            前端发送一个请求给隐藏的iframe,然后服务端每次将返回值,以js形式返回,然后iframe的父窗口获取window.name的值。服务端返回数据形式为:
    <script>window.name={errno:0, errmsg:'ok'}</script>
    • document.domain
            这个使用限制条件较多,必须是不同子域间,协议和端口号必须相同。比如:a.12306.cn和b.12306.cn之间相互操作,可以分别在两个域名下定义:document.domain = '12306.cn'; 这样就实现了跨子域通信。
    • 服务端代理
            这种情况不适合跨协议通信,比较适合跨端口和跨域名。这个前端基本上相当于普通的请求,我们所要访问的接口都经过服务端的代理,我们访问的请求都是本域的。
    • jsonp请求
            我们用的比较多,原理就是在发起请求时,动态的在页面加载一个script标签,因为src可以接收跨域资源,然后这个script标签的资源是执行一个js方法,并且将服务端返回的数据作为参数传递过来。这种情况唯一的缺点就是只能发送get请求,不适用用post请求。jsonp返回的数据格式为:
        callback({errno:0,errmsg:'ok'});
    • 前端代理
            和window.name的实现比较类似,将请求发送到一个隐藏的iframe,然后服务端返回类似这样的内容:
    <script>location.href='http://www.12306.cn/proxy.html?func=callback&errno=0&errmsg=ok'</script>

    然后proxy文件,接收到这些参数,进行处理,转化成类似与jsonp的返回值,执行页面上的回调。这种情况是可以发送post请求的。
     
            proxy文件的内容,如下:
    <!doctype html>
    <html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body><script type="text/javascript">
    (function(){
      var queryStr = location.search.substring(1).split('&'),oneQueryStr,args = {},g = parent,scope = parent ,callback;
      for(var i in queryStr){
       oneQueryStr = queryStr[i].split('=');
       if(!callback && oneQueryStr[0] == 'fun'){
        callback = oneQueryStr[1];
       };
       if(oneQueryStr[0]&&oneQueryStr[1]){
        args[oneQueryStr[0]] = (oneQueryStr[1]||'').replace(/[><'"{}]/g, '');
       }
      
      }
      callback = callback.split('.');
      
      if( callback[0] === 'document'
       || callback[0] === 'location'
       || callback[0] === 'alert'){
      }else{
       for(var i = 0,len= callback.length;i<len;i++){
        if(i==0 && callback[0]=="parent"){
         g = parent;
         scope = parent;
        }else if(i==0 && callback[0]=="top"){
         g = top;
         scope = top;
        }else{
         if(i<len-1){
          scope = scope[callback[i]];
         }
         g = g[callback[i]];
        }
       }
      
       g.call(scope,args);
      }
      
    })();
    </script>
    </body></html>

     通过上面的介绍,简单的知道了处理跨域请求的一些方法,下面整理了一个基于jquery的,解决跨域的方法。
    $(function() {
        'use strict';
     
        /**
         * 交互类
         * @param {object} param 要提交的数据
         * @param {Object} [ajaxOpt] ajax配置
         * @param {boolean} [https=false] 是否使用https协议
         * @constructor
         */
        var Sync = function(param, ajaxOpt, https) {
            var protocol = this.protocol = https ? 'https': 'http';
     
            var ajaxOptDefault = {
                url: protocol + '://'+location.host,
                type: 'GET',
                dataType: 'jsonp',
                timeout: 20000
            };
     
            param = string2JSON(param) || {};
     
            this.protocol = protocol;
            this.param = $.extend({}, param, ajaxOpt);
            this.ajaxOpt = $.extend({data: this.param}, ajaxOptDefault);
            this.HOST = protocol + '://'+location.host;
        };
     
        function string2JSON(str){
            if($.isPlainObject(str)) {
                return str;
            }
            var params = {};
            var str2Arr = str.split('&');
            $.each(str2Arr, function(i, keyVal) {
                var arr = keyVal.split('=');
                //去除serialize把空格转成+
                params[arr[0]] = decodeURIComponent(arr[1]).replace(/[+]+/, '').replace(/[+]+/, '');
            });
            return params;
        }
     
        $.extend(Sync.prototype, {
            /**
             * 通过get方式(jsonp)提交
             * @param {String} [url] 请求链接
             * @return {Object} promise对象
             */
            get: function(url) {
                var self = this;
                var send = $.ajax(url, this.ajaxOpt);
                return send.then(this.done, function(statues) {
                    return self.fail(statues);
                });
            },
            /**
             * 通过post方式提交,依赖psp_jump.html文件
             * @param {String} [url] 请求链接
             * @return {Object} promise对象
             */
            post: function(url) {
                var deferred = $.Deferred();
                var timer = null;
                var guid = parseInt(new Date().getTime().toString().substr(4), 10);
                var funName =  'QBXJsonp' + guid;
                var formName = 'QBXForm' + guid;
                var iframeName = 'QBXIframe' + guid;
     
                // iframe 不能使用attr('name', xxx) 否则在ie7上添加的不是name属性而是submitName
                var iframe = $('<iframe name="' + iframeName +'">').hide();
                var param = $.extend({}, this.param, {proxy: this.HOST+'/proxy.html', callback: funName});
                var form = buildForm(param, {name: formName, target: iframeName, url: url || this.ajaxOpt.url});
     
                window[funName] = function (data){
                    clearTimeout(timer);
                    var value;
                    for (var i in data) {
                        if (data.hasOwnProperty(i)) {
                            value = decodeURIComponent(data[i]);
                            if (value.match(/^({.*})|([.*])$/)) {
                                value = $.parseJSON(value);
                            }
                            data[i] = value;
                        }
                    }
                    deferred.resolve(data);
                };
     
                timer = setTimeout(function(){
                    deferred.reject({
                        errno: 999999,
                        errmsg: '网络超时,请稍后重试'
                    });
                }, this.ajaxOpt.timeout);
     
                // do some clear
                deferred.always(function(data) {
                    $(iframe).remove();
                    $(form).remove();
                    // IE8以下不支持delete window属性
                    try {
                        delete window[funName];
                    } catch (e){
                        window[funName] = null;
                    }
                });
                $(document.body).append(iframe).append(form);
                $(form).submit();
                return deferred.then(this.done);
            },
            /**
             * 收到响应时默认回调
             * @param {Object} data 数据
             * @return {Object}
             */
            done: function (data) {
                var deferred = $.Deferred();
                if (data.errno == 0) {
                    deferred.resolve(data);
                } else {
                    deferred.reject(data);
                }
                return deferred.promise();
            },
            /**
             * 未收到响应时默认回调
             * @param {Object} error 错误信息
             * @return {Object}
             */
            fail: function(error) {
                var deferred = $.Deferred();
                deferred.reject({
                    errno: 999999,
                    errmsg: '网络超时,请稍后重试'
                });
                return deferred.promise();
            }
        });
     
        /**
         * 把数据对象转成form元素
         * @param {Object} data json数据
         * @param {Object} opts form所需参数或其他数据
         * @return {Object}
         */
        function buildForm(data, opts) {
            if (opts.url) {
                opts.action = opts.url;
                delete opts.url;
            }
            opts.method = 'post';
            var $form = $('<form>').attr(opts).hide();
            $.each(data, function (name, value) {
                $('<input>').attr({type: 'hidden', name: name, value: value}).appendTo($form);
            });
            return $form[0];
        }
         
        window.Sync = Sync;
     
    });


    这样我们就可以通过一下方式进行使用:
    var login = function(data) {
        var sync = new Sync(data);
        return sync.post(sync.HOST+'/login');
    }
      
    login({username: 'blackMao', password: 'blackMao'})
        .done(function() {
            alert('登陆成功!');
        })
        .fail(function(error) {
            alert(error.errmsg);
        });


     至此就可以做到跨域请求了~
  • 相关阅读:
    什么是理想?
    leetcode 62. 不同路径-动态规划及优化,双100%
    使用双指针暴力解决力扣28题《实现 strStr()》
    使用双指针解决力扣27题《移除元素》
    SQL SERVER 数据库日志已满时清理日志的方法
    修改git提交的名字和邮箱
    React Native运行出现Could not find "iPhone X" simulator
    eosio 编译与部署
    恢复经常写博客的习惯
    MAC OS系统替换homebrew使用阿里云或中科大的镜像源
  • 原文地址:https://www.cnblogs.com/xinting/p/12536171.html
Copyright © 2011-2022 走看看