Web 跨域请求

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

  1. 只关注发送,不关注接收
  2.不仅要发送,还要关注服务端返回的信息

      a.  同域请求
      b.  跨域请求

所谓 跨域,一般情况下为三种情况:跨协议、跨子域、跨域名。下面距离梳理一下这三种情况。

跨协议:比如说我现在的域名地址是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);
	});
至此就可以做到跨域请求了
时间: 2024-07-30 20:30:34

Web 跨域请求的相关文章

Web 跨域请求(OCRS) 前端解决方案

1.同源策略如下: URL 说明 是否允许通信 http://www.a.com/a.jshttp://www.a.com/b.js 同一域名下 允许 http://www.a.com/lab/a.jshttp://www.a.com/script/b.js 同一域名下不同文件夹 允许 http://www.a.com:8000/a.jshttp://www.a.com/b.js 同一域名,不同端口 不允许 http://www.a.com/a.jshttps://www.a.com/b.js

Django跨域请求CSRF的方法示例

web跨域请求 1.为什么要有跨域限制 举个例子: 1.用户登录了自己的银行页面 http://mybank.com,http://mybank.com向用户的cookie中添加用户标识. 2.用户浏览了恶意页面 http://evil.com.执行了页面中的恶意AJAX请求代码. 3.http://evil.com向http://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去. 4.银行页面从发送的cookie中提取

基于.Net Framework 4.0 Web API开发(5):ASP.NET Web APIs AJAX 跨域请求解决办法(CORS实现)

概述:  ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作.但是在使用API的时候总会遇到跨域请求的问题,特别各种APP万花齐放的今天,API的跨域请求是不能避免的. 在默认情况下,为了防止CSRF跨站的伪造攻击(或者是 javascript的同源策略(Same-Origin Policy)),一个网页从另外一个域获取数据时就会收到限制.有一些方法可以突破这个限制,那就是大家熟知的JSONP, 当然这只是

web三种跨域请求数据方法

web三种跨域请求数据方法 以下测试代码使用php,浏览器测试使用IE9,chrome,firefox,safari <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.

.Net Core 3.0 Api json web token 中间件签权验证和 CORS 中间件处理跨域请求

第一步:在Nuget上安装"Microsoft.AspNet.WebApi.Cors"包,并对api controller使用[EnableCors]特性以及Microsoft.AspNetCore.Authentication.JwtBearer包 第二步:创建.netcore API项目 /控制器:AuthenticateController using System; using System.Collections.Generic; using System.Linq; usi

$.ajax 跨域请求 Web Api

WepApi确实方便好用,没有配置文件,一个apicontroller直接可以干活了.但今天用$.ajax跨域请求的时候总是获取不到数据,用fiddler一看确实抓到了数据,但回到$.ajax函数中,直接触发了error,没有触发success,即使状态码是200.用apiclient或者浏览器直接访问都是ok的.搜罗一番.最终在这篇文章上面找到答案 .http://code.msdn.microsoft.com/windowsdesktop/Implementing-CORS-support-

JSonP跨域请求

我们在通过自己的页面或程序通过ajax请求其它网站或服务时,会存在一个ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面.动态网页.web服务.WCF,只要是跨域请求,一律不准.不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>.<img>.<iframe>).   于是可以判断,当前阶段如果想通过纯web端(ActiveX控件.服

解决前端跨域请求的几种方式

利用 JSONP 实现跨域调用 说道跨域调用,可能大家首先想到的或者听说过的就是 JSONP 了. 1.1 什么是JSONP JSONP 是 JSON 的一种使用模式,可以解决主流浏览器的跨域数据访问问题.其原理是根据 XmlHttpRequest 对象受到同源策略的影响,而 <script> 标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料.用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句. 1.2

Ajax 跨域请求 jsonp获取json数据

遇到Ajax的跨域请求出问题 找了中解决办法如下: 参考内容:http://justcoding.iteye.com/blog/1366102 由于受到浏览器的限制,该方法不允许跨域通信.如果尝试从不同的域请求数据,会出现安全错误.如果能控制数 据驻留的远程服务器并且每个请求都前往同一域,就可以避免这些安全错误.但是,如果仅停留在自己的服务器上,Web 应用程序还有什么用处呢?如果需要从多个第三方服务器收集数据时,又该怎么办? 理解同源策略 同源策略阻止从一个域上加载的脚本获取或操作另一个域上的