JavaScript常见跨域思路代码

本文转载自知字而智《详解跨域问题》
        前两天,有个朋友说他公司需要利用跨域来解决一些问题,他自己不是很清楚,问了我,我只记得当初鹏鹏老师教过利用jsonp来解决跨域问题,但是那会没实际去操作跟应用。相应的理解也就停留在最表层上,后来经过自己去看了相关资料,总结出了以下三个解决跨域的方法,供小伙伴们一起学习跟交流!

首先,跨域是指:协议、域名、端口有任何一个不同,都被当做是不同的域。举一个最简单的例子:a.text.com跟b.text.com就是两个不同的域,之间所发生的任何交互都叫做跨域请求。那么在我们本地如何来设置跨域模拟环境呢,别着急,听哥慢慢说,你只需要点击我的电脑,打开C:\\window/system32\drivers\ect里面的hosts文件,就是这个配置文件,那这个配置文件到底干了些什么呢,这边我们还是得先了解什么是DNS解析过程,域名是如何解析的?这么来说吧,假如你在浏览器输入www.test.com后,那么它背后又是干了些什么呢,我们的电脑又是如何来解析这个域名的呢?来,咋们一个一个说。首先它会到本机文件系统里寻找一个映射文件,有木有被这个映射文件名字给整懵了,其实这货就是上面我们说到的hosts。然后这个文件配置了映射关系,简单讲就是某个域名对应着某个一个IP,如果在我们配置文件找不到当前域名对应IP这么一个映射关系,它才会向网络上的DNS服务器发起解析的请求,让服务器返回当前域名对应的IP是什么。所以搞清楚这些以后,我们只需要修改hosts就能完成模拟环境的设置!那么接下来才是重头戏了~
我们来说第一种解决跨域的方法:通过iframe协助完成跨子域
就拿上面我们提到的两个东西a.text.com跟b.text.com,如果你直接在a页面对b进行访问,那么结果就是这样,我截图啦:
6630593876189864142,有没有看懂这意思呢,实在不行就请出我们班的唯一四级先生!Mrs炎~!其实傻逼都知道,那是跨子域出现的报错。那么我们怎么来解决呢,既然ajax不能解决跨子域问题,那么问题来了,跨子域技术哪家强,iframe强强强!其实最基本的解决思路在于通过提升域的级别,把两个页面都提升到test.com这个根域,那么这时候浏览器就会认为它们两个就是同一个域,接着我们利用iframe通信这个媒婆来进行协助完成跨域的ajax请求。废话说得差不多了,咋们来看下实际代码:
b域创建一个bb页面:
6630500417700762264
然后再创建另外一个bbb页面,起到中间人的作用,协助两个不同域之间的通信,因为它本身也是属于b里面的一个子域,通过这么个回调函数callback让另外一个域来调用的,代码如下:
6631219498305466231

那么我们另外一个域是如何来调用上面那个回调函数的呢?前面我们已经提到过iframe这个东西,那它又干些什么呢,其实很简单,只是利用iframe里面的src属性,在属性里面我们填写的是bbb页面的url路径,同时iframe只是协助作用,我们又不想看到它,所以我们这么做,把它给display:none掉!来,我们看下代码:

6630428949444956116
那么接下来也是最后一步,就是在bb页面中找到iframe的window引用,然后通过window引用就可以调用另外一个页面的方法,记得上面我们所提到的callback回调函数吗,它就是用来处理请求完成的结果。我们这里传入一个匿名函数,这个匿名函数的作用是处理对方请求已经完成的结果,所以当这个函数被调用的时候,其实data就是从另外一个域传回的数据,这样就实现了我们两个域之间的数据交换。有没有萌萌哒?哈哈,来,我们看代码:
6631318454351966940
现在有没有豁然开朗了呢?没有也没关系,我给你总结一下:实际ajax还是在b域里面来完成的,我们只是借助iframe来干了实现数据在不同域之间跨域传递这么一件事。说到最重点的就是这句:document.domain = ‘test.com’,可别小看这货,它作用是相当的大,它是把当前域提升到根域这个级别,我们前面说到了,在不同域之间是不能进行数据传递的,正因为我们通过这货来提升域为test.com这个根域,来让两个不同子域来变成同一个域,最后两个页面就可以进行通信了。
好啦!我们接着来学习第二种跨域的方法:通过jsonp实现跨域
谈到这块大家肯定都不陌生,特别是我们h5-5这群小子,那么我在这里也当做是复习,再给大家梳洗一下利用jsonp实现跨域的核心关键点。首先我们需要理解的是jsonp在跨域过程中干了那些事?从本质上讲,其实jsonp并不是通过ajax来解决跨域,而是执行跨域js。有没有明白这个意思呢?不明白也没关系,我们接下来一个一个来讲:
前面我们也提到iframe实现跨域的最根本就是利用src属性来实现的。没错,script这货也是干了同样的事,如果你想把它换成img、iframe来实现也是可以的,因为它们都具备src这个属性!当然那我们为什么还选择script这个家伙呢,其实它只是更合适,我们可以通过script加载其他域的一段动态脚本,这段脚本可以包含我们想要的数据信息,这样就一石二鸟,一箭双雕了是吧。用它就对了!来,我们还是看代码:
6631319553863595181
这边我们封装了一个jsonp方法,它可以实现动态生成script标签跟删除script标签。以及实现回调函数的绑定,这边你就需要清楚后台这个回调函数是干啥的,在前端中,这个callback是匿名函数,但是在后端它在返回的时候,它必须知道回调函数的名称是什么,才能正确的调用,是不是觉得脑袋不够用了,晕菜了吧!没事,咋们看下代码就知道了:
后端代码:
6631337146049628525

看到这里,应该明白了为什么我们传入的回调函数为啥要处理一下,让它产生随机名称,接着把这个随机名称作为window的属性给它存储起来,同时让这个window属性指向callback,到这里我们就相当于给匿名函数起了一个名,这样后台就可以调用得到啦!

然后我们要进行跨域的操作就变得简单了。假如我们要向b这个页面进行跨域请求,那么我们只需要调用jsonp这个函数,在url位置上填写b的路径,代码如下:
6630083702793837020

最后面我们执行了一个onload来实现删除script标签。到这里我们也解决了跨域问题了,有没有惊呆了,难道这么简单?没错,就是这么简单。

接着我们来加个菜~  如果你很熟悉jQuery的开发文档,应该知道getJSON()这个东西,其实它也是可以实现跨域请求的,同样的它也有三个参数,第一个参数也是url,第二个参数data,第三个参数是callbakc回调函数。那么接下来就依瓢画葫芦了,我们先来看下代码:
6630588378631730359

这里面有一个关键点:就是url后面要跟一个函数名等于?这样一个参数,只有这样getJSON()才是以jsonp的形式发起;当然如果你没有跟上一个函数名等于?这样一个参数,那么就认为它只是同域中的ajax请求,是无法完成跨域的。

 
介绍完getJSON()那么我们接下来继续学习利用底层的jquery.ajax方法发起请求,同样可以达到跨域的效果。我们来看下代码:
6630847863375895371

这边跟getJSON()的差别主要是定义了dataType的类型为jsonp类型,其他地方都是一样的。其实它还可以这么写,我们先看代码,在解释原因:

6630339889003106108

这种方法是使用jsonp和jsonpCallback来代替上面的callback匿名函数,jsonp指定的是函数的名称,就如上面的callback,也可以用其他的名字;jsonpCallback是指定参数的值,上面是参数值是随机生成的,所以用“?”来代替。好了,到这里我们利用jsonp来解决跨域也差不多了,同样的,既然它那么好用,那么有利必有弊,jsonp实现跨域有啥缺点呢?这里我们总结了两点:第一点是只支持get方式;第二种是后端代码要调整。其实呢,在最上面我们已经提高过,jsonp跨域的本质是通过script标签来和其他域发生交互,而且script标签只支持get方式的请求,所以这也是jsonp跨域的一大缺点。

扯了也快两个小时了,不知道你们看懂了多少。当然,既然要学就得真的学会,自己可以尝试去敲下代码验证下!好了,最后我们来个压头戏,我们再讲一个跨域解决方法:CORS跨域资源共享
说到这货大家肯定不陌生,因为它本质上就是XMLHttpRequest对象原生的跨域ajax支持。整个思路你可以这样理解:举个简单的例子,咋们上班狗不得都要打卡上班吗?如果打卡机没有采集你的指纹,那么你是无法完成身份验证的。同理,如果我们请求的目标域能够给我们一个允许的标志,那么我们就可以进行相应的跨域访问了!原理搞清楚了,那接下来就好办了。首先地球人都知道ajax请求默认是get请求方式进行的,那么我们先以这种情况来进行跨域,我们代码如下:
6630317898770553670

看到这,你想到了什么,咋们执行代码会出现什么情况呢?deng~deng~deng~

6630879749213090825

哈哈~  报错?不是吧?这么可能?没错,是报错,我们这样只是简单进行ajax请求,进行跨域当然会报错啦,但是呢,这个报错是非常有必要的,细心的你如果仔细看,会发现报错的原因在于缺少一个头,就是这句代码:Control-Allow-Origin。太感谢浏览器了,这货竟然帮我们把问题找出来了。其实这个东西是为了跨域安全问题,需要在服务器响应头部提供一些信息,供浏览器校验请求是否被允许。那么接下来就好办了,我们只需要在我们目标请求b.test.com在返回的时候给我们加一个头信息,并且头信息是允许我们当前所在的域b.test.com来发起访问。那么接下来我们只需要修改请求目标的后端代码,代码如下:

6630798385352633335

这边只要在请求头里面把a.test.com设置为可以被访问的对象,这样它就可以实现跨域请求了。如果实现多个域只要用“*”来代替就行啦。

刚才上面我们已经说过我们的发起形式是已get形式发起的,那么如果我们使用post或者put发起呢,会出现什么乱子呢?我们看代码:
6630878649701463253

这边一样会报错,一样的我们根据浏览器反馈给我们得信息进行分析,得出了Access-Control-Allow-Methods。粗浅的理解就是没有这个权限呗!那不是很好办嘛,同理我们在后端代码上做下修改不就成了!代码如下:

6631242588049653375

这样修改完,就可以使用POST/PUT等请求方式进行跨域请求啦。到此为止,我们针对跨域这问题也算是详细得分析了一遍了,如果乡亲们还是不解,那么我也给您跪了。真心带不动啊!!!


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流