本文深入解析JavaScript中的跨域问题,阐述同源策略中协议、域名和端口号的判定规则。详细讲解JSONP利用script标签实现跨域的原理及回调函数机制,CORS后端Access-Control-Allow-Origin头配置方法,以及通过Nginx服务器代理解决跨域请求的完整流程。
前言
学习JavaScript的开发者都知道,当我们在浏览器地址栏输入网址并发送网络请求时,浏览器会自动跳转到对应页面。例如输入www.baidu.com,浏览器会跳转至百度首页。浏览器发送网络请求需要三个基本要素。
完整的URL路径实际上包含协议、域名和端口号。例如http://www.baidu.com:80,浏览器在发送请求时默认会补全协议(http)和端口号(80)。因此,完整的URL至少包含三个部分。
1. http/https 传输协议
2. www.baidu.com 域名
3.80 端口号
正是基于这三个基本要素,才产生了跨域请求的概念。所谓跨域,是指当传输协议、域名或端口号中任意一个或多个不相等时,就会触发浏览器的同源策略。同源策略要求三者完全一致,属于同一源头。
补充说明
- 同源策略由浏览器强制执行。
浏览器限制向其他服务器发送请求,仅允许向自身服务器发送请求。若尝试向其他服务器发送请求,浏览器会进行阻止。
- 什么是“其他服务器”?
当请求的协议、域名或端口号中任意一个存在差异时,即视为访问其他服务器,此时会触发同源策略。
既然浏览器阻止跨域请求(即限制访问其他服务器),我们需要寻找解决浏览器限制的方法,以实现跨域请求。
在实际案例中,跨域请求十分常见。例如,A网站需要引用B网站的图片资源,最佳方式是向B网站服务器请求这些图片。当A网站需要B网站的数据时,便形成了跨域请求。
**
如何实现跨域请求?
**
方法一:JSONP
在学习JSONP技术前,需了解一个机制:通过script标签的src属性引入文件时,无论文件类型如何,都会被解析为字符串并作为JavaScript代码执行。利用这一特性,可以实现跨域请求,具体案例如下。
假设有一个a.text文件,其中包含一行代码:
alert('a')

我们在另一个网页中引用这个a.text文件。

页面加载后弹出提示框显示“a”,说明a.text文件中的代码已成功执行。

基于此原理,如果我们请求的是服务器上的文件,无需关心其后缀名,只要服务器返回数据,前端即可获取。接下来进行具体操作。
当我们请求另一台服务器的数据时,例如:
http://10.36.136.96:80/ 是目标服务器地址。
1.php 是目标服务器上的文件,我们尝试正常发起请求。
在目标服务器上存在一个名为1.php的文件。
<script>
//http://10.36.136.96:80/是另一台服务器的地址
//1.php是另一台服务器上的文件,我们正常去请求
var xhr = new XMLHttpRequest()
xhr.open('get','http://10.36.136.96:80/1.php')
xhr.onload = function () {
console.log(xhr.responseText);//1.php文件将要返回的结果
}
xhr.send()
</script>
此时浏览器会报错,提示触发了同源策略,出现跨域请求错误。

此时我们可以利用前文提到的script标签的src属性,通过script标签向服务器文件发起请求。
在json.html文件中添加一行代码:
<script src="http://10.36.136.96:80/1.php"></script>
可以看到浏览器未报错,成功请求到文件并获取内容。但问题来了,如何获取返回的具体结果?
这里可以巧妙运用回调函数。

后端返回数据时,可以通过调用函数的方式来执行。
1.php文件后端的代码如下:
<?php
$_GET['callback'];
echo "$name('sanqian')";
?>
json.html文件前端的代码如下:
<script>
function fn(data){
console.log(data);
}
</script>
<script src="http://10.36.136.96:80/1.php?callback=fn"></script>
callback等号后面的值为回调函数名,将其传递给1.php文件。后端获取该函数名后,调用传入的数据并返回结果,从而在前端通过函数调用的方式轻松获取数据。
也就是说,我们在前端定义了一个名为fn的全局函数。注意,必须是全局函数,因为函数名指向该函数的指针。将函数名传递过去,后端执行操作后返回fn('sanqian'),该结果被前端作为JavaScript代码执行。上述代码等价于:
<script>
function fn(data){
console.log(data);
}
</script>
<script>
fn('sanqian')
</script>
这样便实现了先定义后执行调用的过程,从而成功获取后端返回的结果。

JSONP的原理和使用方法如上,目前应用较为广泛,建议掌握。
方法二:CORS
这是一种纯后端实现的方法,与前端代码无关。
当我们直接向另一台服务器请求数据时,浏览器会报出经典错误:

翻译过来即:

访问被禁止。我们需要将请求的资源地址放入Access-Control-Allow-Origin响应头中。
若需返回结果[sanqian],前后端代码调整如下:
json.html文件代码:
<script>
var xhr = new XMLHttpRequest()
xhr.open('get','http://10.36.136.96:80/1.php')
xhr.onload = function () {
console.log(xhr.responseText);
}
xhr.send()
1.php文件代码:
<?php
header('Access-Control-Allow-Origin:*');
echo "sanqian";
?>
只需添加这一行代码,即可正常请求到目标资源。
当然,还可以配置更多代码,例如:
//表明允许跨域访问
header(Access-Control-Allow-Origin:上面origin的地址)//*号代表所有
header('Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE'); //支持的http 动作
header('Access-Control-Allow-Headers:x-requested-with,content-type'); //响应头 请按照自己需求添加
这是跨域请求中最简单的方法,且完全由后端处理。
方法三:服务器代理
Proxy(代理)
代理跨域(服务器正向代理跨域),因为浏览器不允许请求非同源地址。
在浏览器同源限制下,可以引入代理服务器:将本应发送给目标服务器的请求发送给代理服务器,由代理服务器转发请求给目标服务器,目标服务器将响应返回给代理服务器,最后由代理服务器将响应返回给浏览器。
任何服务器均可进行代理,但Apache代理HTTP无需费用,代理HTTPS需付费。
而Nginx对HTTP和HTTPS代理均免费,因此使用Nginx进行代理演示。
首先需要在Nginx服务器上配置好路径。

在配置文件的
server{}块内
serve{
...
location = /sanqian {
proxy_pass http://10.36.136.96:80/1.php;
}
...
}

配置完成后,json.html文件的代码变为:
<script>
var xhr = new XMLHttpRequest()
xhr.open('get','/sanqian') // 注意这里变了
xhr.onload = function () {
console.log(xhr.responseText);
}
xhr.send()
</script>
1.php文件代码变为:
<?php
echo "sanqian";
?>
/sanqian 是本机服务器上的路径。当请求路径为/sanqian时,服务器识别出实际目标是访问http://10.36.136.96:80/1.php,代理服务器随后访问该地址,获取结果后返回给当前访问的浏览器,从而成功获取数据。

以上是对跨域请求的简单理解,内容可能不全或有疏漏,欢迎指正,谢谢!
总结
至此,关于JavaScript中跨域问题的文章介绍完毕。更多相关JavaScript跨域问题内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!