跨域了?别着急,手把手带你从问题调研到解决!– 文件上传跨域问题(OSS)

这篇文章的主题是如何带着问题去解决问题,不会解释一些简单的概念。
此文的前提是你对跨域的基本理解是 OK 的。

如果你是新手朋友,不要着急,后续我会针对跨域问题进行抽丝剥茧的讲解,欢迎关注~
下一篇文章的部分内容:

  • 同源策略
    • “源” 到底是什么?
    • 什么是同源策略?
    • 由来及相关文献。
    • 跨域都会影响哪些内容?
  • 跨域
    • 到底是什么在影响我们的请求?
    • 简单请求与非简单请求
    • 后端解决方案
    • 前端解决方案(JSONP、反向代理、postMessage、ws、iframe)
  • 代理
    • 什么是代理?
    • 正向代理、反向代理 的区别是什么?

前言

作为一个开发者,我相信大家都对带着问题去学习深有体会。
如果你是个新入门的开发者,那么你一定躲不开的问题就是学习 git ,在学习的过程中,一定会发现,数不清的命令要记。
记住第二个忘记第一个,不停的在做 狗熊掰棒子 的事儿。
在你真正开始工作之后发现,之前学习的 git 命令全忘光了。随用随查反而学会了 git 的使用。

😡ps:天赋异禀过目不忘的人走开走开~

那么下面的讲解我将遵循以下逻辑:

  1. 现状概述、需求分析
  2. 问题调研
  3. 解决方案
  4. 写代码

那么我们开始吧!

背景

现状概述

跨域实现

我目前手头有一个 react 项目,项目的 api 一律是 api.sincenir.com ,那么对于 react 来讲,直接通过 package.json 来解决跨域的问题显然是最优解:

{ 
    ...,
    'proxy': 'https://api.sincenir.com' 
}

接口封装

我这里的接口请求还是使用的 axios ,哈哈这么不 react 的方案,主要的原因是为了多项目统一接口封装。具体细节就不讲了,看看伪代码吧~

const configMap = { 
    'local': { 
        ... 
        host: '' 
    }, 
    'production': { 
        ... 
        host: 'https://api.sincenir.com' 
    } 
}

const getDevType = () => { ... }
const env = getDevType()

axios.baseUrl = configMap[env].host

需求分析

现状讲完了,接下来我们讲讲需求,明确我们的需求,我们才能定位问题、解决问题。

我们最近来了一个新需求,需要用到上传,我们的文件服务器采用的是 OSS ,这里的 OSS 你可以当作成 七牛 或者自己的服务器,都没有关系。

知道我们有了上传需求,接下来我们就需要明确我们的文件上传都需要做什么。

首先,必不可少的就是 Upload 组件,及对应的上传地址等内容。与自己的服务器上传不同的是,我们还需要在上传前通过接口获取 OSS 的签名、上传地址;还需要在上传成功后告诉我们的服务器。

那么我们的上传过程就是:

  1. 调用 oss.sincenir.com/oss/signature 取 OSS 的上传签名、上传地址等内容
  2. 通过第一步接口获取到的内容,调用 Upload 组件上传文件
  3. 调用 oss.sincenir.com/oss/uploadafter 上传文件信息等内容到自己的服务器

问题

通过上面的需求分析,我们会发现,我们需要干的只有三件事。

  • 调用 GET 接口 oss.sincenir.com/oss/signature
  • 调用 OSS 的文件上传接口
  • 调用 POST 接口 oss.sincenir.com/oss/uploadafter

通过上面的三件事,具体问题具体分析。
第一件事是最简单的,调用一个非 proxy 域名的简单请求。直接请求就好了。
第二件事是比较简单的,就是非 proxy 域名的复杂请求。第一个问题,多域名跨域怎么解决?
第三件事是比较难的,难的点在于,我们的 OSS 限制了请求的主域名为 sincenir.com ,限制域名又该怎么解决?

问题分析

问题1:多域名跨域如何解决?

react

如果我们采用了其他的跨域解决方案,可能就不会出现该问题。
我们采用的方案是 proxy 做反向代理,解决的跨域问题。
该方案是由 react 的脚手架提供的,理论上我们可以通过配置多 proxy 解决多域名跨域问题的。
但是我们大多数人的 create-react-app 是超过 2.0 版本的。超过 2.0 版本会导致一个问题,就是 package.json 中只能配置 string 类型。因而无法使用该方法。

那么剩下的方法就是通过 http-proxy-middleware 解决该问题。

超过 2.0 版本的 create-react-app 会导致 package.json 中只能配置 string 类型
PS: 这里具体没有细研究原因,计划在跨域大篇章中进行详细调研。

解决方案:

  1. 安装 http-proxy-middleware
  2. 新建 setupProxy.jssrc 目录下
  3. 配置 setupProxy.js

vue

配置 vue.config.js 文件即可。

问题2:访问的服务器做了域名限制怎么办?

这里我当时的直觉是,我们直接让服务器提供接口,前端上传至服务器,服务器再上传至 OSS 服务器。

当然这个方案需要后端配合,主要的工作量便跑到了后端同学那里。于是,我去找了后端同学讲了该问题。
就在后端同学准备开发接口时,乔哥跟我讲可以配置虚拟域名进行上传接口的请求阿。我当时都懵了。我还跟乔哥抬杠,虚拟域名可以请求的话 OSS 的域名限制还有啥用?这也太不安全了,浏览器内核应该不允许这个方案的。

直到乔哥给我发了一篇:阿里云OSS上传文件本地调试跨域问题解决
我都懵了,还真行吗?写这篇文章的肯定是问题解决了,那我就跟着试试吧。

教程就不复制了,很简单的几件事:

  1. 配置本地的 127.0.0.1 映射到 sincenir.com
  2. 配置运行端口(react、vue有些许区别)
  3. 重启项目
  4. 访问 sincenir.com
  5. 正常上传文件

PS:事实证明,人真的不能凭经验、凭本能做事,万事在没有做过之前,要先调研清楚,再做决定。
这样可以很大程度避免进行无用的工作(沟通成本、后端开发成本)。

实现

既然方案都定了,跟着方案走即可。

多域名跨域

react

安装 http-proxy-middleware

执行安装命令

# --save-dev 的原因是,我们只需要本地启动时,通过反向代理解决跨域
npm install http-proxy-middleware --save-dev

创建 setupProxy.js 文件

project/src 目录下创建,setupProxy.js ,这里的 project 指的是你的项目。

为了避免大家出错,这里可以理解为和 pagesviews 目录平级。

配置 setupProxy.js

const { createProxyMiddleware } = require('http-proxy-middleware')

module.exports = function (app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'https://api.sincenir.com',
      changeOrigin: true
    })
  )
  app.use(
    '/oss',
    createProxyMiddleware({
      target: 'https://oss.sincenir.com',
      changeOrigin: true
    })
  )
}

需要注意的一点是:
如果项目开启了严格模式,禁止了 require 命令,直接使用 eslint 的忽略。
// eslint-disable-next-line @typescript-eslint/no-var-requires

vue

vue 多域名就更简单了,直接修改 vue.config.js 文件。

module.exports = {
  ...
  devServer: {
    proxy: {
      "/api": {
        target: "http://api.sincenir.com",
        changeOrigin: true,
        ws: true,
        crossorigin: "anonymous",
        pathRewrite: {
          "^/api": "/api",
        },
      },
      "/oss": {
        target: "https://oss.sincenir.com",
        changeOrigin: true,
        ws: true,
        crossorigin: "anonymous",
        pathRewrite: {
          "^/oss": "/api",
        },
      },
    },
  },
  ...
};

到这里,我们两个域名的跨域问题就解决啦~

访问服务器的域名限制问题

配置本地的 127.0.0.1 映射到 sincenir.com

前往本地的 C:\Windows\System32\drivers\etc 文件夹下,找到 hosts 文件,添加一行映射:

127.0.0.1 sincenir.com

需要注意:

  • # 是文件的注释,配置时,需要保证端口前无 #
  • 该文件需要以管理员身份打开、编辑、保存,可以通过 vscode 编辑保存,vscode 会提示你授权管理员权限

优化:hosts 管理工具

为了避免我们频繁修改 hosts 系统文件,进而导致的问题,我们可以通过 hosts 的管理工具来处理 hosts 文件。

  1. 下载 SwitchHosts 点击下载
  2. 安装并打开
  3. 添加 OSShost 跨域了?别着急,手把手带你从问题调研到解决!-- 文件上传跨域问题(OSS)
  4. 配置 OSShost 跨域了?别着急,手把手带你从问题调研到解决!-- 文件上传跨域问题(OSS)
  5. 启动 OSShost 跨域了?别着急,手把手带你从问题调研到解决!-- 文件上传跨域问题(OSS)

SwitchHosts(hosts管理工具)git地址:github.com/oldj/Switch…

配置项目运行端口为 80

react

方案1:
node_modules\react-scripts\scripts\start.js 文件中,将下面代码替换:\

const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 8080;
// 替换为
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 80;

方案2:
修改 package.json 中的 start 命令:

{
    "scripts": {
        "start": "set PORT=80 && craco start",
    }
}

vue

配置 vue.config.js

module.exports = {
  ...
  devServer: {
    ...
    // 修改端口
    port: 80,
    // 老版本 vue(解决 Invalid Host header 问题)
    disableHostCheck: true
    // 新版本 vue(解决 Invalid Host header 问题)
    historyApiFallback: true,
    allowedHosts: ["test.fangcunyisheng.com"],
  },
  ...
};

后期调试

配置完成后首先需要重启项目,然后不要打开启动后的 127.0.0.1:80 或者 localhost 的页面,打开 sincenir.com ,到这里就完成啦~

最后

这篇文章的主要讲的是带着问题如何解决问题,第二讲的是 OSS 跨域上传如何解决。

后续的文章会刨根问底的讲一讲跨域的由来、解决方案、及周边生态。

欢迎大家 点赞、关注、收藏~

原文链接:https://juejin.cn/post/7241938328840716325 作者:sincenir

(0)
上一篇 2023年6月8日 上午10:36
下一篇 2023年6月8日 上午10:46

相关推荐

发表回复

登录后才能评论