在了解Ajax同源策略之前,我们首先需要知道Ajax请求限制的问题。
什么是Ajax请求限制?
Ajax 只能向自己的服务器发送请求,如果想要向其他服务器发送请求就涉及到跨域请求的问题。
比如现在有一个A网站、有一个B网站,A网站中的 HTML 文件只能向A网站服务器中发送 Ajax 请求,B网站中的 HTML 文件只能向 B 网站中发送 Ajax 请求,但是 A 网站是不能向 B 网站发送 Ajax请求的,同理,B 网站也不能向 A 网站发送 Ajax请求。
在解决这个问题之前,我们就要去理解同源策略的相关概念
Ajax同源策略是由Netscape(网景通信公司)提出的安全策略,它可以让同一网站之间的访问不受限制,但不同网站之间不能随意访问。
Ajax同源策略规定URL地址中的协议(http/https)、域名和端口要都相同,以保证多个页面或者请求来自同一个服务器端,也就是同源,只要有一个不相同,就是不同源。
URL:
URL 通常由几个部分组成,包括协议、服务器地址、端口号、文件路径和查询字符串等,例如
http://www.example.com:8080/index.html?name=Bob
协议:
超文本传输协议(HyperText Transfer Protocol),服务器和浏览器之间传输超文本(HTML、图片等内容)的传送协议。
- HTTP(Hypertext Transfer Protocol):用于在 Web 浏览器和 Web 服务器之间传输文本、图片、音频、视频等内容。
- HTTPS(HTTP Secure):是一种基于 SSL/TLS 协议的安全传输协议,可以保证数据传输的安全性。
域名:
在互联网中,域名(Domain Name)是用于便于记忆的网络地址,通常用于访问万维网上的网站。它是由一串用点分隔的名字组成的,例如
www.example.com
。其中,.com
称为顶级域名(TLD),example
是一个二级域名,www
则是三级域名。域名通过一个域名系统(DNS)来解析成实际的 IP 地址,以便计算机之间进行通信访问。具体来说,当我们在浏览器中输入一个域名时,浏览器会首先向本地 DNS 服务器发送一个域名解析请求,如果该服务器无法解析,则会向更高层次的 DNS 服务器发送请求,直到找到正确的 IP 地址为止。最终,该 IP 地址用于建立与服务器的连接,以便获取网页内容等资源。
端口号:
端口通常是一个 16 位无符号整数,取值范围为 0 到 65535。
其中,0 到 1023 的端口号被称为“知名端口”(Well-known Port),一般用于标识系统和服务的通信端口,例如 HTTP 使用的默认端口号为 80 。
例如:
- http://www.example.com/dir/page.html(默认端口号是80)
- http://www.example.com/dir2/other.html:同源
- http://example.com/dir/other.html:不同源(域名不同)
- http://v2.www.example.com/dir/other.html:不同源(域名不同)
- http://www.example.com:81/dir/other.html:不同源(端口不同)
- https://www.example.com/dir/page.html:不同源(协议不同)
在code目录下新建server1目录,然后再server1中新建public目录,在public中新建demo.html页面
DOCTYPE html>
<html lang="en">
<head>
<title>(server1)演示两个不同源服务器之间发送Ajax请求的限制title>
head>
<body>
<button id="btn">点我发送请求button>
<script src="../js/ajax.js">script>
<script>
// 获取btn
var btn = document.getElementById('btn');
// 为按钮添加单击事件
btn.onclick = function () {
// 调用ajax函数
ajax({
// 表示访问端口号为3001的不同源的server2服务器
url: 'http://localhost:3001/test',
success: function (data) {
console.log(data)
}
})
}
script>
body>
html>
在server1目录下,安装express框架,新建app.js
安装express npm init -y npm install express --save npm list express
// 引入express模块
const express = require('express')
// 引入path模块处理路径
const path = require('path')
// 创建网站服务器对象
const app = express()
// 定义中间件处理静态资源
app.use(express.static(path.join(__dirname, 'public')))
// 监听端口
app.listen(3000)
console.log('服务器已启动,请访问localhost:3000')
在code目录下,新建server2目录,在server2目录下安装express框架,并新建app.js文件
// 引入express模块
const express = require('express')
// 创建网站服务器对象
const app = express()
// 定义路由
app.get('/test', (req, res) => {
res.send('ok')
})
// 监听端口
app.listen(3001)
console.log('服务器已启动,请访问localhost:3000')
分别切换到server1目录和server2目录,在命令行工具中使用nodemon app.js命令分别启动server1和server2两个不同源的服务器
在浏览器中访问“http://localhost:3000/demo.html”,点击按钮,查看控制台报错信息
JSONP(JSON with Padding)是JSON的一种使用模式,它解决了Ajax同源限制的问题。
JSONP请求使用
标签来实现。
语法
设置src属性的值为JSONP请求地址,当页面加载时会发起了一个JSONP的跨域请求
<script type=“text/javascript” src=“JSONP请求地址">
</script>
JSONP请求地址的组成
/jsonp
:表示路由的地址
?
:表示连接请求参数
&
:表示连接多个参数
jsonpcallback
:表示请求参数名称设置name参数的值为value
http://localhost:3000/jsonp?jsonpcallback=callbackFunction&name=value
示例代码
演示JSONP实现跨域请求
在server1\public目录下新建demo1.html文件,编写HTML代码。
使用
向http://localhost:3001/jsonp发起JSONP跨域请求
服务器还使用server1目录下app.js进行启动
DOCTYPE html>
<html lang="en">
<head>
<title>演示JSONP实现跨域请求title>
head>
<body>
<script>
function fn(data) {
console.log('客户端的fn函数被调用了')
}
script>
<script src="http://localhost:3001/jsonp">script>
body>
html>
在server2目录中,新建jsonp.js文件,编写JavaScript代码
定义“/jsonp”路由
// 引入express模块
const express = require('express')
// 创建网站服务器对象
const app = express()
// 定义路由
app.get('/jsonp', (req, res) => {
// 发送fn()函数的调用
res.send('fn()')
})
// 监听端口
app.listen(3001)
console.log('服务器已启动,请访问localhost:3001')
切换到server2目录,使用nodemon jsonp.js启动服务器,同时切换到server1目录下,使用nodemon app.js启动服务器
在浏览器中打开“http://localhost:3000/demo01.html”,查看运行结果。
fn函数被调用说明,可以进行跨域请求
request模块是一个Node.js第三方模块,是对http模块的封装,使用起来更加方便。
使用request模块能够解决服务器端同源限制的问题,它解决跨域请求问题的基本过程。
使用Node.js中的request模块在服务器端向其他不同源服务器发送请求。
不同源服务器将结果返回给同源服务器。
在同源服务器中,再把数据返回给浏览器端。
示例代码
下载和安装request模块
npm install request --save
server1\public目录,新建demo2.html文件,编写HTML代码。
<body>
<button id="btn">点我发送请求button>
<script src="/js/ajax.js">script>
body>
demo2.html文件中,编写HTML代码。
调用ajax()函数,向http://localhost:3000/server发送GET请求
<script>
// 获取btn
var btn = document.getElementById('btn');
// 给btn绑定事件
btn.onclick = function () {
ajax({
type: 'get',
url: 'http://localhost:3000/server',
success: function (data) {
console.log(data)
}
})
}
</script>
在server1目录中,新建server.js文件,编写JavaScript代码。
- 当客户端请求
http://localhost:3000/server
时,调用request
函数向另一个服务器http://localhost:3001/cross
发送 GET 请求,并将获取到的响应数据返回给客户端。
// 引入 express 模块
const express = require('express');
// 引入 request 模块
const request = require('request');
// 引入 path 模块
const path = require('path');
// 创建 express 实例
const app = express();
// 静态资源访问服务功能
// 设置静态资源根目录为 'public' 文件夹
app.use(express.static(path.join(__dirname, 'public')));
// 定义 /server 路由处理方法
app.get('/server', (req, res) => {
// 调用 request 模块向 http://localhost:3001/cross 发送 GET 请求
request('http://localhost:3001/cross', (err, response, body) => {
// 将响应的数据返回给客户端
res.send(body);
});
});
// 监听 3000 端口,启动服务器
app.listen(3000);
console.log('服务器启动成功');
新建server2目录,在该目录下新建cross.js文件,编写JavaScript代码。
定义“/cross”路由
// 引入Express框架
const express = require('express');
// 创建Web服务器
const app = express();
app.get('/cross', (req, res) => {
res.send('ok');
});
// 监听端口
app.listen(3001);
// 控制台提示输出
console.log('服务器启动成功');
切换到server1和server2目录,在命令行工具中分别使用nodemon server.js和nodemon cross.js分别启动两个不同源的服务器
在浏览器中打开“http://localhost:3000/demo2.html”,单击“点我发送请求”
CORS:全称为 Cross-origin resource sharing,即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 只能同源使用的限制。
响应头
// 允许 http://localhost:3000 域名的跨域访问 Access-Control-Allow-Origin: 'http://localhost:3000' // 如果要允许多个域名,请逐个添加,并在各个域名之间用逗号隔开或使用通配 * Access-Control-Allow-Origin: '*'
使用CORS解决跨域问题,通常需要在服务端进行配置。
在服务端发送响应时,需要设置正确的CORS响应头,以允许跨域访问。
示例代码
在server1\public目录下,新建demo3.html
DOCTYPE html>
<html lang="en">
<head>
<title>演示cors解决跨域请求问题title>
head>
<body>
<button id="btn">点我发送请求button>
<script>
// 获取btn
var btn = document.getElementById('btn');
// 给btn绑定事件
btn.onclick = function () {
// 创建XMLHttpRequest对象
var xhr = new XMLHttpRequest()
// 初始化请求
xhr.open('get', 'http://localhost:3001/cors')
// 设置允许跨域携带cookie
xhr.withCredentials = true
// 请求成功回调
xhr.onload = function () {
if (xhr.status == 200) {
console.log(xhr.responseText)
}
}
// 发送请求
xhr.send()
}
script>
body>
html>
在server2目录下,新建cors.js文件
const express = require('express');
const app = express();
// 允许所有源的跨域访问,并允许携带身份凭证(如 cookie)
app.use((req, res, next) => {
// 允许http://localhost:3000 跨域访问
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
// GET请求处理
app.get('/cors', (req, res) => {
res.send('CORS 连接成功!');
});
// 启动Web服务器
app.listen(3001, () => {
console.log('Web服务器正在监听 3001 端口...');
});
在这个例子中,通过调用Express的 use
方法设置一个中间件,将CORS响应头设置为允许http://localhost:3000的跨域访问,并允许携带身份凭证。next()
方法表示继续执行下一个中间件。
在处理GET请求时,通过回调函数(handler)返回一个字符串“CORS 连接成功!”。 注意,如果要对其他类型的请求(如POST、PUT、DELETE)进行CORS处理,需要在响应头中设置 Access-Control-Allow-Methods
头。
总的来说,使用CORS解决跨域问题需要在服务端进行配置,同时需要在客户端进行一些指定头部处理。在服务端进行正确的配置后,就可以允许跨域请求,并与其他域名下的网站进行正常交互了。
切换到server1和server2目录,在命令行工具中分别使用nodemon app.js和nodemon cors.js分别启动两个不同源的服务器
在浏览器中打开“http://localhost:3000/demo3.html”,单击“点我发送请求”
如何解决跨域问题?
总结
名称 | 描述 |
---|---|
CORS | 跨域资源共享,需要后端配合设置 Access-Control-Allow-Origin 字段,允许指定的域名跨域请求数据 (了解即可) |
JSONP | 利用浏览器不限制 script 标签跨域的特性,通过 script 标签的 src 属性加载跨域文件 |
request | request模块是一个Node.js第三方模块,是对http模块的封装,使用起来更加方便。使用request模块能够解决服务器端同源限制的问题,它解决跨域请求问题的基本过程。 |