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

        在前端开发过程中,难免和服务端产生数据交互。一般情况我们的请求分为这么几种情况:
    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>
    

      2014-11-18:

      @pyzy:window.name的值必须是字符串,需要使用JSON.stringify处理传递,获取之后可以用JSON.parse处理进行使用

    • 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?fun=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);
        });
    

      至此就可以做到跨域请求了~

     
  • 相关阅读:
    centos 安装 TortoiseSVN svn 客户端
    linux 定时任务 日志记录
    centos6.5 安装PHP7.0支持nginx
    linux root 用户 定时任务添加
    composer 一些使用说明
    laravel cookie写入
    laravel composer 安装指定版本以及基本的配置
    mysql 删除重复记录语句
    linux php redis 扩展安装
    linux php 安装 memcache 扩展
  • 原文地址:https://www.cnblogs.com/xiaoheimiaoer/p/4101588.html
Copyright © 2011-2022 走看看