webpack 代理 proxy 还是跨域?

proxy代理了浏览器发起的请求还是跨域,但是复制 curl 后在终端执行正常,本文将对这个跨越问题进行分析与解决

proxy 配置

jsdoc在js中获得类型提示,方便编辑器智能补全

js 复制代码
/** @type {import('webpack-dev-server').ProxyConfigMap} */
const proxyConfig = {};
js 复制代码
/**
 * 根据环境变量的代理配置
 * process.env.VUE_APP_PROXY_ENV(.env[.xxx]文件配置)
 * npm script启动的时候 --mode会读取相应的 .env[.xxx]文件,设置环境变量
 * 连接到不同环境调试的时候通过设置相对的环境变量,读取代理配置
 */
module.exports = function generateProxyConfig(isProduction) {
  /** @type {import('webpack-dev-server').ProxyConfigMap} */
  const proxyConfig = {};
  pathToTarget.forEach((item) => {
    proxyConfig[item.startPath] = {
      target: item.target,
      secure: true,
      changeOrigin: true,
      pathRewrite: item.pathRewrite || {},
      headers: {
        "X-Forwarded-Host": VUE_APP_API_URL.replace(/https?:\/\//, ""),
      },
      onProxyReq: (proxyReq, req, res) => {
        if (proxyReq.method === 'OPTIONS') {
          proxyReq.destroy();
          res
            .setHeader(
              'Access-Control-Allow-Headers',
              'Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With'
            )
            .setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE, PUT')
            .status(200)
            .end();
        }
      },
      onProxyRes: (proxyRes, req, res) => {
        // 查看proxyRes
        console.log(proxyRes);
      },
    };
  });
  if (!isProduction) console.table(proxyConfig);
  // 保存到文件供查看代理配置
  saveConfigJson(proxyConfig);
  return proxyConfig;
};

发起请求

  • req: 原始请求
  • proxyReq: 代理请求
  • proxyRes: 代理请求的响应,此时的 reqproxyReq 一致

使用 curl 发起请求

  • 终端执行 curl 发起请求
bash 复制代码
$ curl 'http://localhost:7200/v1/template?offset=0&limit=20'   -H 'sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"'   -H 'sec-ch-ua-mobile: ?0'   -H 'Authorization: xxxxx=='   -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'   -H 'Content-Type: application/json'   -H 'Accept: application/json, text/plain, */*'   -H 'Referer: http://localhost:7200/'   -H 'sec-ch-ua-platform: "macOS"'   --compressed

curl 内容如下(与上面的一样):

bash 复制代码
> curl 'http://localhost:7200/v1/template?offset=0&limit=20' \
>   -H 'sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"' \
>   -H 'sec-ch-ua-mobile: ?0' \
>   -H 'Authorization: xxxxxx==' \
>   -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' \
>   -H 'Content-Type: application/json' \
>   -H 'Accept: application/json, text/plain, */*' \
>   -H 'Referer: http://localhost:7200/' \
>   -H 'sec-ch-ua-platform: "macOS"' \
>   --compressed
  • curl 发起的原始请求与代理请求的头部 header

浏览器发起请求

在浏览器的 network 那里看请求其实是有Authorization头的

  • network 复制出来的原始请求 fetch
js 复制代码
fetch("http://localhost:7200/v1/template?offset=0&limit=20", {
  headers: {
    accept: "application/json, text/plain, */*",
    authorization: "Basic xxxx",
    "content-type": "application/json",
    "sec-ch-ua":
      '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"macOS"',
  },
  referrer: "http://localhost:7200/",
  referrerPolicy: "strict-origin-when-cross-origin",
  body: null,
  method: "GET",
  mode: "cors",
  credentials: "include",
});
  • 浏览器发起的代理请求头部 header

可以看到

  • 浏览器发起的原始请求,少了鉴权的Authorization头,导致代理请求也少了

  • 然后在onProxyRes里面看到proxyRes的状态码是401

  • 加上Authorization头之后,发现是此时的proxyRes的状态码是404,是后端未设置OPTIONS请求导致

  • curl请求与浏览器请求的差异是没有OPTIONS请求

简单请求

简单请求

复杂请求CORS

简单请求外就是复杂请求,浏览器会发起CORS 预检请求

服务器未设置允许OPTIONS请求的话,浏览器会识别为跨域

触发OPTIONS请求

CORS的预检请求-OPTIONS

告知服务器,是否支持新增或修改请求头/请求方式,如下:

一般请求会设置 Content-Type: application/json,这个是最容易忽略的

跳过OPTIONS请求

这里主要是拦截OPTIONS请求(proxyReq.destroy()),不发送到服务器,直接返回结果

设置响应头

js 复制代码
onProxyReq: (proxyReq, req, res) => {
  if (proxyReq.method === 'OPTIONS') {
    proxyReq.destroy();
    res
      .setHeader(
        'Access-Control-Allow-Headers',
        'Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With'
      )
      .setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE, PUT')
      .status(200)
      .end();
  }
}

最后

其实这个问题是需要后端接口处理的,这里也算是在开发服务器处理了;

但是生命在于折腾,通过经过分析具体了解了跨域的原因,解决跨域的问题,也是一种学习和乐趣

相关推荐
deming_su2 小时前
第5篇:EggJS中间件开发与实战应用
javascript·经验分享·中间件·node.js
2c237c61 天前
使用Node编写轻量级后端快速入门
后端·arcgis·node.js·c5全栈
吴永琦(桂林电子科技大学)1 天前
Node.js心得笔记
笔记·node.js
敲敲敲-敲代码1 天前
【 Node.js】 Node.js安装
node.js
Attacking-Coder1 天前
前端面试宝典---webpack原理解析,并有简化版源码
前端·面试·webpack
玄晓乌屋1 天前
nvm for windows 安装低版本 node 丢失 npm 安装
前端·npm·node.js
SparklingTheo1 天前
npm init、换源问题踩坑
前端·npm·node.js
F2E_Zhangmo1 天前
webpack5启动项目报错:process is not defined
前端·vue.js·webpack·webpack5
万物得其道者成1 天前
使用 Vue3 + Webpack 和 Vue3 + Vite 实现微前端架构(基于 Qiankun)
前端·webpack·架构
agenIT2 天前
Webpack 相关用法与总结
前端·webpack·node.js