跨域
定义:
- 协议,域名,端口任意一个不同, 都不属于同源,相互之间 的数据请求 称之为跨域请求
同源限制
- 无法发ajax 请求 无法共享cookie
option请求:
-
浏览器针对非简单请求的跨域 请求会主动向目标服务器先发起一个预检请求OPTIONS
-
其目的就是为了判断实际发送的请求是否是安全的。
-
什么是简单请求
- 不是 get也不是post的时候 基本就都是复杂请求
- 当get或者post 请求 自定义了 请求头的时候也会变成复杂请求,使得浏览器触发预检机制
node实现cors:
初始化
- npm init -y
- live-server: vscode插件安装,永远另外起一个不同端口的服务器做跨域请求准备
html
- btn.addEventListener 监听click事件
- click事件 里 xhr = new XMLHttpRequest()
- xhr.open(“POST”, “http://localhost:3000/login“, true);实现动态文件请求
- 静态文件:浏览器对服务器进行简单不会因为请求操作而发生变化的(html,js,img..),;
- 动态文件:浏览器访问这个资源,资源的源代码可能会发送改变
- 通过setRequestHeader 将post 请求变成复杂请求
setRequestHeader和responseType的区别
:
前者规定客户端向服务端传递的数据类型;后者规定的是客户端要求服务器需要返回的数据类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="btn">点击</button>
<script>
btn.addEventListener("click", () => {
const xhr = new XMLHttpRequest();
//我这里的默认服务器端口是3000
xhr.open("POST", "http://localhost:3000/login", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.responseType = "json";
xhr.onload = function () {
//浏览器会根据返回的类型进行解析
console.log(xhr.response);
};
// 传递的数据格式是 字符串类型
xhr.send('{"name":"ZOE"}');
})
</script>
</body>
</html>
复制代码
js
-
创建服务,监听端口3000
-
判断是否是跨域请求
是否存在 请求头origin,跨域请求 浏览器 会自带origin 请求头
是:
-
Access-Control-Allow-Origin 白名单配置
-
Access-Control-Allow-Headers 允许被自定义的请求头
-
Access-Control-Max-Age 固定时间内的的跨域请求 可以无需重复发起option请求(
浏览器里Disable cache 不能勾选 要允许 本地缓存,否则看不到效果
) -
option请求的时候 做空值返回的处理
否:
- 获取请求路径 判断是文件还是文件目录
- 都不是 返回404
- 如果是文件目录 找到 目录下对应的index.html &fs.access 判断是否可访问,
不可访
问同样返回404;可访问
用可读流(fs.createReadStream)+管道(pipe) 进行文件内容读取,利用mime 获取文件内容格式,并设置编码规范为utf-8 - 如果是文件 直接用可读流(fs.createReadStream)+管道(pipe) 进行文件内容读取 默认text/html 格式 + utf-8编码
做普通请求的处理
-
const http = require("http");
const url = require("url");
const fs = require("fs");
const path = require("path");
const mime = require("mime");
const server = http.createServer((req, res) => {
let { pathname, query } = url.parse(req.url, true);
//跨域情况下,允许访问的处理
if (req.headers.origin) {
//实践应用场景肯定是判断是否是服务端预设的白名单里的来源,这里简单处理 用于效果呈现
res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
res.setHeader("Access-Control-Max-Age", "10");
res.setHeader("Access-Control-Allow-Credentials", true);
if (req.method === "OPTIONS") {
return res.end();
}
}
// 动态文件请求逻辑代码
if (pathname === "/login" && req.method === "POST") {
//文件读取
req.on("data", (chunk) => {
buffer.push(chunk);
});
//根据浏览器 不同的content-type 的要求 显示不同格式的数据,
req.on("end", () => {
let buf = Buffer.concat(buffer); // 浏览器传递来的数据
if (req.headers["content-type"] === "application/json") {
let obj = JSON.parse(buf.toString()); //回显为Json字符串
res.setHeader("Content-Type", "application/json");
//写入响应给浏览器的数据
res.end(JSON.stringify(obj));
} else if (req.headers["content-type"] === "text/plain") {
res.setHeader("Content-Type", "text/plain");
//写入响应给浏览器的数据
res.end(buf.toString());
} else {
//其他格式的content-type 暂不支持
res.end("ok");
}
});
}else {
//静态文件请求逻辑代码
let filePath = path.join(__dirname, pathname);
// 获取请求路径 判断是文件还是文件目录
fs.stat(filePath, function (err, statObj) {
// 请求错误 没有找到对应url资源 返回404
if (err) {
res.statusCode = 404;
res.end("NOT FOUND");
} else {
// 如果是文件,用可读流+管道 pipe 进行文件内容读取,利用mime 获取文件内容格式,并设置编码规范为utf-8
if (statObj.isFile()) {
res.setHeader(
"Content-Type",
mime.getType(filePath) + ";charset=utf-8"
);
fs.createReadStream(filePath).pipe(res);
} else {
//如果是文件目录 找到 目录下对应的index.html
let htmlPath = path.join(filePath, "index.html");
//fs.access判断拼接的路径是否可访问
fs.access(htmlPath, function (err) {
if (err) {
// 不可访问 设置 状态码404
res.statusCode = 404;
res.end("NOT FOUND");
} else {
// 可访问,用可读流加管道 pipe 进行文件内容读取
res.setHeader("Content-Type", "text/html;charset=utf-8");
fs.createReadStream(htmlPath).pipe(res);
}
});
}
}
});
}
})
server.listen(3000, () => {
console.log("server start 3000");
});
复制代码
测试结果
- 成功实现跨域请求
- 10秒内不重复发起option请求
- 这里的Disable cache 不能勾选 要允许 本地缓存,否则看不到效果
小结
跨域的解决 有很多种,例如:jsonp ,iframe,nginx方向代理,websocket; 但 cors 应该是 应用相对比较多了的 跨域解决方法,所以有了今天的这篇实践+总结
最后如果觉得本文有帮助 记得点赞三连哦 十分感谢
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END