产生跨域的原因
前后端不分离
在早期的开发中 前后端是不分离的 因此我们只有一台服务器 浏览器去请求页面的时候在我们的后端服务器就会把数据渲染好然后返回给我们,像这种服务器渲染页面就不会出现跨域问题,因为在那个时候就一台服务器后端和前端代码是在一起的没有什么所谓的请求。
那个时期流行JSP开发
JSP融合了html标签、JSTL标签、Java代码,在进行开发的时候,前端的同事在进行界面布局之后,后端同事需要在前端的界面上写Java代码才能完成前端界面的完整业务。这样高强度耦合的方式,直接导致开发、联调时间增加,这也就是JSP时代的诟病。
前后端分离
随着ajax的出现我们前端工程师就可以专注于页面的搭建想要数据就去发请求拿数据,后端也不用前端 专心写后端的逻辑代码
总结原因
当前端和后端分离以后 后端开发好接口部署到一个服务器上,前端写好页面部到一个服务器上,这样前端需要数据就需要到另外一个服务器请求数据,这时候就会触发浏览器的安全策略 (同源策略) ,同源策略是一个重要的安全策略 它用于限制于一个orgin的文档 或者他加载的脚本如何能与另一个源的资源进行交互。他能帮助我们阻挡恶意的文档,减少被可能攻击的媒介。比如,如果没有同源策略我们在一号服务器向二号服务器发请求拿到登录数据,万一有个黑客想干你,劫持数据把该登录请求再发一份发到他自己的服务器上 很容易就能拿到客户的用户名和密码 很不银杏!如果不想产生跨域问题 你要遵守一个方案叫做元祖,该方案被称为 / 协议 / 主机 / 端口元祖 也就是这哥仨要一样 有一个不一样就是不安全的不给你请求数据。怎么样才算一样呢? 就是浏览器在访问静态资源服务器时候的哥仨和在发请求获取数据时候的哥仨要一样,这就是跨域产生的原因!
解决方案一:
为啥不把接口和页面部署在同一个服务器解决跨域呢?
将接口和页面部署在同一台服务器可以解决跨域问题,因为它们共享相同的主机、端口号和协议。这样浏览器在请求静态资源和后端接口时就不会存在跨域限制(这不就是很久以前单体架构的项目嘛)。然而,有时候接口和页面需要分别部署在不同的服务器上,这可能是因为系统架构的需要、负载均衡的考虑或者其他原因。在这种情况下,就需要通过其他方式来解决跨域问题,例如使用代理服务器、JSONP、CORS(跨域资源共享)等技术。这种一台服务器部署就不要想了。
解决方案二:
CORS方式解决跨域问题
- 它是一种基于http header的机制
- 该机制通过允许服务器表示除了他自己以外的其他源 域 协议和端口 是的其他浏览器允许这些orgin访问加载自己的资源
服务端代码(Koa为例)—解决非简单请求和简单请求跨域访问
const Koa = require('koa')
const KoaRouter = require('@koa/router')
const static = require('koa-static')
const app = new Koa()
// 中间件
app.use(async (ctx, next) => {
// 1.允许简单请求开启CORS 第二个参数是允许访问的源 *表示都允许访问
ctx.set("Access-Control-Allow-Origin", "*")
// 2.非简单请求开启下面的设置
// 这行表示 携带的请求头
ctx.set("Access-Control-Allow-Headers", "Accept, Accept-Encoding, Connection, Host, Origin,Accept-Language,Content-Type,Content-Length,Referer,User-Agent")
ctx.set("Access-Control-Allow-Credentials", true) // 这行表示可以跨域携带cookie cookie
ctx.set("Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, PATCH, OPTIONS") //这行表示允许的请求
// 3.如果发起的是一个options请求
if (ctx.method === 'OPTIONS') {
ctx.status = 204 //返回该状态码是为了告诉浏览器我允许OPTIONS请求
} else {
await next()
}
})
const userRouter = new KoaRouter({ prefix: '/users' })
userRouter.get('/list', (ctx, next) => {
ctx.body = [
{ id: 111, name: "why", age: 18 },
{ id: 112, name: "kobe", age: 18 },
{ id: 113, name: "james", age: 25 },
{ id: 114, name: "curry", age: 30 },
]
})
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
app.listen(8000, () => {
console.log('koa服务器启动成功~')
})
请求代码
<script>
// 1.XHR网络请求
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
console.log(JSON.parse(xhr.responseText))
}
}
xhr.open('get', 'http://localhost:8000/users/list')
xhr.send()
// 2.fetch网络请求
fetch('http://localhost:8000/users/list').then(async res => {
const result = await res.json()
console.log(result)
})
</script>
解决方案三:
Node代理服务器 (开发中用的比较多)
由于跨域问题是浏览器的锅 服务器与服务器是没有跨域问题的所以我们可以开启一个代理服务器去解决跨域问题
// node服务器代理
// webpack => webpack-dev-server
const express = require('express')
//引入代理中间件
const { createProxyMiddleware } = require('http-proxy-middleware')
const app = express()
//这一行是将我的测试请求代码部署到当前服务器目的是为了静态资源和代理服务器在一台服务器
//然后由我们的代理服务器去转发请求实现解决跨域
app.use(express.static('./client'))
app.use('/api', createProxyMiddleware({
//转发服务器地址
target: "http://localhost:8000",
// 需要代理的api ^表示正则语法 匹配/api开头的api
pathRewrite: {
'^/api': ''
}
}))
app.listen(9000, () => {
console.log('express proxy服务器开启成功')
})
请求代码
<script>
// 1.XHR网络请求
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
console.log(JSON.parse(xhr.responseText))
}
}
xhr.open('get', 'http://localhost:9000/api/users/list')
xhr.send()
// 2.fetch网络请求
fetch('http://localhost:9000/api/users/list').then(async res => {
const result = await res.json()
console.log(result)
})
</script>
其实webpack 底层也是采用这种方案来解决跨域的
解决方案四:nginx反向代理解决跨域问题(实际项目部署会使用这种方案)
nginx本质上也是通过请求转发来实现跨域问题解决
浏览器下载Nginx 解压双击exe即可开启
浏览器访问http://localhost/
看见这些就说明启动成功
我们需要去配置一下Nginx
nginx-1.22.1\conf\nginx.conf 我们需要配置一下这个文件
每一次你配置好了Nginx服务器后你都要重启一下服务器 直接干掉这些进程就行
现在你只要发请求 访问localhost他就会给你转发请求
前端代码
<script>
// 1.XHR网络请求
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
console.log(JSON.parse(xhr.responseText))
}
}
xhr.open('get', 'http://localhost/users/list')
xhr.send()
// 2.fetch网络请求
fetch('http://localhost/users/list').then(async res => {
const result = await res.json()
console.log(result)
})
</script>
我这里只是配置了简单请求如果你需要配置非简单请求你需要和CORS里面那样配置非简单请求
不能少写分号
如果你配置完成后需要重启服务器建议用git bush 重启那样的话可以看见我们的代码有没有写错