前端如何解决跨域问题?

作者:五月君 来源:Nodejs技术栈

前端如何解决跨域问题?

文章插图
 
“前端如何解决跨域问题?” 这个是前段在知乎看到的一个提问,这几乎是做前端都会遇到的一个问题,产生的情况可能会很多,解决一个问题还是要先了解下为什么会产生这样问题,学习最好的方法就是结合一些实际的案例来学习,理解和掌握也会更加的深刻,本文结合 Node.js 写一些 Demo 看一下跨域问题及解决办法,最好是自己看完也能够动手操作下~
Cross-origin Resource Sharing 中文名称 “跨域资源共享” 简称 “CORS”,它突破了一个请求在浏览器发出只能在同源的情况下向服务器获取数据的限制 。
本文会先从一个示例开始,分析是浏览器还是服务器的限制,之后讲解什么时候会产生预检请求,在整个过程中,也会讲解一下解决该问题的实现方法,文末会再总结如何使用 Node.js 中的 cors 模块和 Nginx 反向代理来解决跨域问题 。
文中使用 Node.js 做一些 Demo 的演示,每一小节之后也会给予代码的 Demo 地址 。
浏览器还是服务器的限制先思考下,CORS 是浏览器端还是服务器端的限制?为了更好地说明这个问题,从一段示例开始 。
从一段示例开始
index.html
<body><!-- <script src=https://www.isolves.com/it/cxkf/bk/2021-06-09/"https://cdn.bootcdn.net/ajax/libs/fetch/3.0.0/fetch.min.js"> --> client.js
创建 client.js 用来加载上面 index.html 。设置端口为 3010 。
const http = require('http'); const fs = require('fs'); const PORT = 3010; http.createServer((req, res) => {fs.createReadStream('index.html').pipe(res); }).listen(PORT); server.js
创建 server.js 开启一个服务,根据不同的请求返回不同的响应 。设置端口为 3011 。
const http = require('http'); const PORT = 3011;http.createServer((req, res) => {const url = req.url;console.log('request url: ', url);if (url === '/api/data') {return res.end('ok!');}if (url === '/script') {return res.end('console.log("hello world!");');} }).listen(PORT);console.log('Server listening on port ', PORT); 测试分析原因运行上面的 client.js、server.js 浏览器输入 http://127.0.0.1:3010 在 Chrome 浏览器中打开 Network 项查看请求信息,如下所示:
前端如何解决跨域问题?

文章插图
 
左侧是使用 fetch 请求的 127.0.0.1:3011/api/data 接口,在请求头里可以看到有 Origin 字段,显示了我们当前的请求信息 。另外还有三个 Sec-Fetch-* 开头的字段,这是一个新的草案 Fetch Metadata Request Headers[1] 。
其中 Sec-Fetch-Mode 表示请求的模式,通过左右两侧结果对比也可以看出左侧是跨域的 。Sec-Fetch-Site 表示的是这个请求是同源还是跨域,由于我们这两个请求都是由 3010 端口发出去请求 3011 端口,是不符合同源策略的 。
看下浏览器 Console 下的日志信息,根据提示得知原因是从 “http://127.0.0.1:3010” 访问 “
http://127.0.0.1:3011/api/data” 被 CORS 策略阻止了,没有 “Access-Control-Allow-Origin” 标头 。
前端如何解决跨域问题?

文章插图
 
在看下服务端的日志,因为请求 3011 服务,所以就看下 3011 服务的日志信息:
Server listening on port3011 request url:/script request url:/api/data 在服务端是有收到请求信息的,说明服务端是正常工作的 。
我们也可以在终端通过 curl 命令测试下,在终端脱离浏览器环境也是可以正常请求的 。
$ curl http://127.0.0.1:3011/api/data ok! 本节代码示例:
github.com/qufei1993/http-protocol/tree/master/example/cors/01 总结回答最开始提出的问题浏览器限制了从脚本内发起的跨源 HTTP 请求,例如 XMLHttpRequest 和我们本示例中使用的 Fetch API 都是遵循的同源策略 。
当一个请求在浏览器端发送出去后,服务端是会收到的并且也会处理和响应,只不过浏览器在解析这个请求的响应之后,发现不属于浏览器的同源策略(地址里面的协议、域名和端口号均相同)也没有包含正确的 CORS 响应头,返回结果被浏览器给拦截了 。
预检请求
预检请求是在发送实际的请求之前,客户端会先发送一个 OPTIONS 方法的请求向服务器确认,如果通过之后,浏览器才会发起真正的请求,这样可以避免跨域请求对服务器的用户数据造成影响 。


推荐阅读