前端跨域

139 阅读4分钟

前言

我们开发前端项目的过程中会遇到跨域的问题,比如浏览器控制台这个报错:

1678266337717.png

今天我们可以浅聊一下跨域的问题。老规矩,带上今天的问题一起来学习一下吧。
  1. 什么是跨域?
  2. 为什么会产生跨域的问题?
  3. 早期的开发有跨域问题吗?
  4. 跨域问题有哪些常见的解决方案?

什么是跨域

定义: 跨域指的是不同服务器之间不能相互访问各自的资源或者数据。

要想理解跨域,要先理解浏览器的同源策略:

  • 同源策略是浏览器一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
  • 如果两个URL的协议、主机地址、端口都相同的话,则这两个URL是同源的。

为什么会产生跨域的问题

事实上跨域问题的产生和前端发展有很大的关系。

  • 早期页面是由服务端渲染完成的,那是没有跨域的问题的;
  • 但是随着前后端的分离,前端开发的代码和服务端开发的API接口往往是分离的(不同的端口),甚至部署在不同的服务器上的;

浏览器发现静态资源和API接口请求不是来自同一个地方时,就产生了跨域。

跨域问题的几种解决方案

跨域的解决方案几乎都和服务器有关,单独靠前端基本解决不了跨域,这就是为什么我们前端在实际工作中很少去解决跨域问题。

常见的跨域解决方案:

  1. 静态资源和API服务器部署在同一域中
  2. CORS (跨域资源共享)
  3. node代理服务器
  4. Nginx反向代理

不常见的方案

  • jsonp
  • postMessage
  • websocket
  • ........

方案一:

静态资源和API服务部署在同一个域

前端代码:

fetch("http://localhost:8000/users/list")
     .then((result) => result.json())
     .then((data) => {
         console.log(data);
      });

服务端代码:

1678273451091.png

浏览器访问http://localhost:8000, 显示:

1678273345098.png

方案二:

跨域资源共享(CORS,Cross-Origin Resource Sharing),他是一种基于http header的机制,允许浏览器可以访问标识过的其他源的资源。

CORS把请求分成两类:简单请求和非简单请求

同时满足以下两大条件,就属于简单请求(不满足就属于非简单请求);

  • 请求方法是一下三种之一

    • HEAD
    • GET
    • POST
  • HTTP的头信息不超过以下的几种字段

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

前端代码:

fetch("http://localhost:8000/users/list")
        .then((result) => result.json())
        .then((data) => {
          console.log(data);
        });

服务端代码:

  1. 原生中间件
//koa框架中使用中间件
app.use((ctx,next)=>{
    // 允许简单请求开启CORS
    ctx.set("Access-Control-Allow-Origin","*")
    // 非简单请求开启下面的设置
    ctx.set("Access-Control-Allow-Headers","Accept, AcceptEncoding, Connection, Host, Origin")
    ctx.set("Access-Control-Allow-Credentials", true) // cookie
    ctx.set("Access-Contro-Allow-Methods","PUT, POST, GET, DELETE, PATCH, OPTIONS")
    if(ctx.method === 'OPTIONS') {
        ctx.status = 204
    } else {
        next();
    }
})
  1. 第三方中间件
const cors = require("koa-cors");
app.use(cors());

浏览器显示:

image.png

方案三:

node代理服务器是平时开发中前端配置最多的一种方案。

跨域既然是浏览器的同源策略引起的。那我们就利用服务端不会跨域的特点,让代理服务器和静态资源同域。让代理服务器帮忙转发。

前端代码:

fetch("http://localhost:9000/api/users/list")
        .then((result) => result.json())
        .then((data) => {
          console.log(data);
        });

代理服务器配置:

const express = require('express')
// http-proxy-middleware中间件
const { createProxyMiddleware } = require('http-proxy-middleware')
​
const app = express()
​
app.use(express.static('./client'))
​
// 使用中间件
app.use('/api', createProxyMiddleware({
  target: "http://localhost:8000",
  pathRewrite: {
    '^/api': ''
  },
  changeOrigin: true
}))
​
app.listen(9000, () => {
  console.log('express proxy服务器开启成功')
})

效果如图所示:

1678277173653.png

我们在工作中可能也配置过Vue、React或者Webpack的代理proxy,其实都是对第三方插件做过封装的。

我们可以看到webpack的文档中也有写道:

1678277517995.png

方案四:

Nginx反向代理

下载Nginx地址:nginx.org/en/download…

1678279358834.png

下载后,解压放到一个不带中文的路径下:

1678279116005.png

点击nginx.exe启动,在电脑的任务管理器中有nginx进程就表示启动成功,访问地址localhost:80显示nginx欢迎页面

1678279265695.png

1678279533736.png

修改nginx的配置文件nginx.config

1678279505683.png

1678279629458.png

重启ngnix服务

前端代码

fetch("http://localhost/users/list")
        .then((result) => result.json())
        .then((data) => {
          console.log(data);
        });

服务端代码:

const koa = require("koa");
const koaRouter = require("@koa/router");
​
// 创建服务器
const app = new koa();
​
// 注册路由对象
const userRouter = new koaRouter({prefix: '/users'})
​
userRouter.get("/list",(ctx,next)=>{
    ctx.body = { "name": "kobe"}
})
​
app.use(userRouter.routes());
app.use(userRouter.allowedMethods());
​
// 开启服务器,监听端口
app.listen(8000,()=>{
    console.log("koa服务器开启成功~");
});

浏览器显示:

1678280122313.png

总结:

  1. ngnix更多配置请查阅文档
  2. 开发中用的最多的是方案三:配置代理
  3. 生产环境用方案四:nginx反向代理