1.了解跨域
跨域:就是一个网站中的网页,向另一个域名下的服务器端发送请求
跨域的情况主要包括5种:
(1)域名不同:如:www.a.com 下的网页 --> www.b.com服务器端发送请求
(2)子级域名不同:如:oa.tedu.com下的网页 --> mail.tedu.com服务器端发送请求
(3)端口不同:如:http://localhost:5050下的网页 --> http://localhost:3000服务器端发请求
(4)协议不同:如:http://12306.cn 下的网页 --> https://12306.cn 服务器端发送请求
(5)同一台主机内部发请求,域名与IP之间互访也属于跨域
如:http://localhost:5050下的网页 -> http://127.0.0.1:5050服务器端发送请求
以上虽然都是跨域,但是有些请求是可以跨域发送的;有些是不能跨域发送的:
(1)可以跨域发送的有:
<link href="引用其他网站的css文件"> CDN
<script src="引用其他网站的js文件"> CDN
<img src="引用其他网站的图片">
<a href="跳转到其它网站的网页">
(2)虽然以上请求都是可以跨域发送的,但是最关键的ajax是禁止跨域的,如果直接通过ajax发送跨域请求,则会在控制台看到熟悉的报错:
这句话翻译过来大概意思就是:到xxx的访问从源头(http://127.0.0.1:5500)被CORS策略阻止了:因为在这个请求的资源上没有设置Access-Control-Allow-Origin响应头
ajax跨域请求会报错的原因:
主要是浏览器的同源策略
那什么是同源策略CORS:
CORS(Cross Origin Sharing Resources)
禁止ajax跨域共享资源
① 浏览器允许ajax发送跨域请求
② 浏览器也允许ajax接收服务器端的相应
③ 但是浏览器会检查服务器端响应结果的"来源地址"
1)如果服务器端响应结果的来源地址和当前网页所在的地址完全一样,才允许使用该响应结果
2)如果服务器端响应结果的来源地址和当前网页所在的地址不一样!则禁止使用该响应结果的数据
2.如何解决跨域
这里只讲常用的两种:CORS方式和jsonp方式
(1)CORS方式跨域:
这种方式跨域,只需要服务器端操作,这里以nodejs的express框架为例:
就是让服务器端篡改返回结果上的来源地址,改成和客户端网页所在地址相同
通过cors模块实现
这样做的结果是:返回客户端的数据上就伪装成了和客户端ip和端口号一样的地址了。
可问题是: "Access-Control-Allow-Origin"只能设置一个客户端地址,如果多个网站的客户端都要访问这个服务器端就不行了。
如何解决呢,只需要把"Access-Control-Allow-Origin":"客户端网页所在ip地址和端口号"里面的"客户端网页所在ip地址和端口号"改成"*" 从而达到伪装成任意客户端地址:
res.writeHead(200,{ "Access-Control-Allow-Origin":"*" ... : ... })
不过,大多数网站,由于服务器端保密的要求,用CORS方式跨域就够了,只允许极个别客户端访问就够了。更不会使用*,让所有人随意访问
(2)jsonp方式跨域:
这里需要知道一个前提,<script>只认识js语句,如果后台传过来的是字符串,控制台会报语法错误,因为,该字符串会被js引擎执行。
举个例子:
如果后台传过来的是一条js语句,如:res.write(`alert("这是后台发送的数据")`);
当通过script请求该服务端时,会返回 alert("这是后台发送的数据") ,这个字符串会直接被js引擎执行,页面会弹窗并显示---"这是后台发送的数据"
所以可以通过:
<script src="服务器接口"></script>
来代替ajax发送请求。
方案1 :前后端同一函数名
知道了<script>会直接执行接收到的js语句,我们可以直接在客户端端定义一个函数,该函数用于处理服务端返回的请求,服务端只需要把函数名和函数参数通过js语句的形式发送到客户端即可。
如:
在客户端定义一个函数,用于简单的打印客户端返回的数据:
function show(data){ console.log(data) }
服务端只需要把前端定义好的函数名和所需要的的数据发送到客户端即可:
res.write(`show(${data})`)
客户端通过<script>请求服务端数据,当请求到数据以后,会直接执行函数名为show()参数为服务端的数据的 函数,从而达到跨域的要求。
方案2:函数名通过请求参数的方式发送给服务端
前后端统一函数名的问题是,函数名在服务端固定。因此可以通过函数名通过请求参数的形式发送到服务端。
客户端script请求参数如下:
<script src="127.0.0.1:8888?callback=show"></script>
客户端定义好的函数名 show()通过请求参数的方式发送到了服务端,服务端通过接收客户端传过来的名为callback的参数中保存的函数名,再将该函数名拼接到要返回的函数调用语句中:
var callback="接收的函数名"
...
res.write(`${callback}(${data})`)
方案3:实现函数多次反复执行
方案2 虽然避免了函数名在后端写死的情况,但是只能在页面加载过程中执行一次,无法反复执行。
如一个按钮,每次点击时,都向服务端请求数据,这时候如果用<script src="...">请求数据就只能在页面第一次加载时执行一次请求。
所以可以通过每次点击按钮时,动态添加<script>标签的方式达到多次执行的目的:
<body> <button id="btn">点击</button> <script> btn.onclick=function(){ var script=document.createElement('script'); script.src="http://127.0.0.1:8888?callback=show"; document.body.appendChild(script); } </script> </body>
如此一来,每次点击按钮时,都能添加一个<script>标签并执行一次请求操作,但是这样添加的<script>标签会一直停留在页面上导致多余的<script>标签堆积。因此需要删除多余的标签:
以上代码改写如下:
<button id="btn">点击</button> <script> btn.onclick=function(){ var script=document.createElement('script'); // script.src="http://127.0.0.1:8888?callback=show"; document.body.appendChild(script);
// 删除多余<script>标签 var ls=document.querySelector("script:last-child"); document.body.removeChild(ls); } </script>
jsonp方式跨域的原理基本就这些了。
在jQuery中,对jsonp方式跨域进行了简化,只需要在传入数据类型参数时,写成"jsonp"就行了
$.ajax({
url:"http://127.0.0.1:8888",
type:"get",
dataType:"jsonp",
success:(res)=>{
...
}
})
其实就是,在传入的url后面jQuery自动添加了一个callback="随机函数名的查询字符串",且服务端的工作并没有减少。