目录
网上有很多关于跨域的例子,但是都是讲跨域是什么,但是没有讲清除跨域到底发生在HTTP请求的哪个阶段,本文通过实际例子去理解。
1 服务端
1.1 服务端代码
使用node
的express
编写服务端,当然也可以是其他的,主要是这个简单。
// 1 引入express
const express = require('express');
// 2 创建应用对象
const app=express();
// 3 服务目录
app.all('/server',(request,response)=>{
// 允许的header类型
response.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
// 可以带cookies
response.header("Access-Control-Allow-Credentials", true);
// 返回数据
response.send("你好啊,我是服务器");
console.log("返回服务器数据");
});
// 4 监听端口
app.listen(9527,()=>{
console.log("服务已经启动,9527端口监听...");
})
非常简单的逻辑,如果来了一个HTTP请求,就返回一句话,然后在服务端打印一下。启动服务监听本地9527
端口。
1.2 启动服务
node server.js
1.3 浏览器测试
是没有问题的,服务端也打印出了返回服务器数据
的字样。
2 网页端
2.1 网页代码
<html>
<head>
<meta charset="UTF-8">
<title>跨域测试</title>
</head>
<body>
<button onclick="sendMess()">点击请求资源</button>
<script>
function sendMess() {
//1 创建对象
const xhr = new XMLHttpRequest();
//2 初始化 设置请求方法和url
xhr.open('GET', 'http://127.0.0.1:9527/server');
//3 发送
xhr.send();
//4 事件绑定 处理服务端返回的结果
xhr.onreadystatechange = function () {
//服务器返回结果
console.log(xhr);
}
}
</script>
</body>
</html>
非常简单一个逻辑,一个按钮,点击向服务端http://127.0.0.1:9527/server
请求数据。
2.2 打开网页测试
发生了错误!这就是我们理解跨域的关键,我们一步步分析。
3 错误分析
3.1 网页端
首先看我们的网页请求,我们打开浏览器,发送了HTTP请求访问http://localhost:63342/kuayu/index.html
网页,这个是webstorm
自动生成的端口号,在线上一般是80
端口或者443
端口,不影响,重要的是要知道网页的web服务器是部署在63342端口上。查看网页请求报文的头部也可以发现Host: localhost:63342
。
3.2 服务端
当我们点击了按钮之后,通过XMLHttpRequest
请求服务端http://127.0.0.1:9527/server
的数据。
服务端能够接受到这个请求,并正常返回数据。
- 服务端可以证明:
- 网络请求可以证明:
这里的状态码是200 OK
,说明这一次请求是正常返回了数据的。
3.3 结论
跨域是发生在浏览器端,与网络请求无关!!!
实际上浏览器甚至不支持发送跨域请求到服务器,但是发展到现在W3C制定了CORS标准了,Chrome
已经支持发送跨域请求,但是服务端传回来的报文却没有使用该标准的响应头,所以浏览器无法解析。
4 跨域的场景
这个网上已经总结非常好了,三点:协议、域名、端口。
本文的例子就是网页服务器部署在63342端口,而数据服务器部署在9527端口,是由于端口不同导致的跨域。
5 解决方案
5.1 JSONP
想出来的程序员绝顶聪明,利用script标签本身就支持跨域。
5.2 CORS
既然浏览器不认响应报文,那么就需要制定规则,告诉浏览器这个跨域响应报文你必须解析。
CORS,全称Cross-Origin Resource Sharing,是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。
使用CORS标准需要有两个条件:浏览器支持、服务端支持。浏览器支持跨域网页请求,所以在服务端也要按照标准,把允许跨域访问的标识写进响应报文头告诉浏览器去正常解析它。
//设置允许跨域的域名,*代表允许任意域名跨域
response.header( 'Access-Control-Allow-Origin' , '*');
//跨域允许的请求方式
response.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");