likes
comments
collection
share

webpack 代理 proxy 还是跨域?

作者站长头像
站长
· 阅读数 12

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

proxy 配置

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

/** @type {import('webpack-dev-server').ProxyConfigMap} */
const proxyConfig = {};
/**
 * 根据环境变量的代理配置
 * 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 发起请求
$ 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 内容如下(与上面的一样):

> 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

webpack 代理 proxy 还是跨域?

浏览器发起请求

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

  • network 复制出来的原始请求 fetch
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

webpack 代理 proxy 还是跨域?

可以看到

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

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

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

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

简单请求

简单请求

复杂请求CORS

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

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

触发OPTIONS请求

CORS的预检请求-OPTIONS

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

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

跳过OPTIONS请求

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

设置响应头

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();
  }
}

最后

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

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