You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vite代理引发无限请求循环:URL查询参数改写需求

解决Vite代理修改URL查询参数导致的无限循环问题

需要将URL http://localhost:5173/externesysteme/google?code=123&scope=456&state=789 转换为 http://localhost:5173/externesysteme/google?codeExt=123&scopeExt=456&stateExt=789。原因是Keycloak要求这三个参数带Ext后缀,但从第三方API获取的参数无后缀,只能通过Vite代理实现修改。

当前配置的问题:在vite.config.js中配置代理后,因重定向到同一路径仅修改参数,导致服务器陷入无限请求循环直至崩溃。

当前Vite代理配置

server: {
    proxy: {
      "/externesysteme/google": {
        target: "http://localhost:5173",
        changeOrigin: true,
        configure: (proxy, options) => {
          proxy.on("proxyReq", (proxyReq, req, res) => {
            let url = new URL(req.url, "http://localhost:5173");
            const searchParams = url.searchParams;

            let paramsChanged = false;

            // 检查并修改查询参数
            if (searchParams.has("code")) {
              const value = searchParams.get("code");
              searchParams.delete("code");
              searchParams.append("codeExt", value);
              paramsChanged = true;
            }

            if (searchParams.has("scope")) {
              const value = searchParams.get("scope");
              searchParams.delete("scope");
              searchParams.append("scopeExt", value);
              paramsChanged = true;
            }

            if (searchParams.has("state")) {
              const value = searchParams.get("state");
              searchParams.delete("state");
              searchParams.append("stateExt", value);
              paramsChanged = true;
            }

            if (paramsChanged) {
              const newLocation = url.pathname + "?" + searchParams.toString();

              res.writeHead(301, {
                Location: newLocation,
                "x-rewritten-url": "true",
              });
              console.log("Redirecting to:", newLocation);
              res.end();
            }
          });
        },
      },
    },
  },

此前正常运行的NGINX配置

location /externesysteme/datev {
        if ($args ~ ^(.+&|)code(=.*)$) {
            return 301 "$uri?$1codeExt$2";
        }
        if ($args ~ ^(.+&|)state(=.*)$) {
            return 301 "$uri?$1stateExt$2";
        }
        if ($args ~ ^(.+&|)scope(=.*)$) {
            return 301 "$uri?$1scopeExt$2";
        }
    }

问题根源与解决方案

问题原因

重定向后的新请求依然匹配/externesysteme/google代理规则,导致代理反复触发参数修改逻辑,形成无限循环。NGINX的重定向不会重复触发同一路径的规则判断,但Vite代理会拦截所有匹配路径的请求,包括重定向后的请求。

方案一:直接修改请求URL(推荐)

使用Vite代理的rewrite钩子直接修改请求路径,无需浏览器重定向,从根源避免循环问题。

修改后的Vite配置:

server: {
    proxy: {
      "/externesysteme/google": {
        target: "http://localhost:5173",
        changeOrigin: true,
        rewrite: (path) => {
          const [basePath, queryString] = path.split('?');
          if (!queryString) return path;

          const searchParams = new URLSearchParams(queryString);
          let paramsChanged = false;

          // 修改参数:替换为带Ext后缀的键名
          if (searchParams.has('code')) {
            searchParams.set('codeExt', searchParams.get('code'));
            searchParams.delete('code');
            paramsChanged = true;
          }
          if (searchParams.has('scope')) {
            searchParams.set('scopeExt', searchParams.get('scope'));
            searchParams.delete('scope');
            paramsChanged = true;
          }
          if (searchParams.has('state')) {
            searchParams.set('stateExt', searchParams.get('state'));
            searchParams.delete('state');
            paramsChanged = true;
          }

          return paramsChanged ? `${basePath}?${searchParams.toString()}` : path;
        },
      },
    },
  },

方案二:通过自定义头跳过重复处理

如果必须使用重定向逻辑,可通过检查自定义请求头判断是否已处理过该请求,避免重复触发修改。

修改后的Vite配置:

server: {
    proxy: {
      "/externesysteme/google": {
        target: "http://localhost:5173",
        changeOrigin: true,
        configure: (proxy, options) => {
          proxy.on("proxyReq", (proxyReq, req, res) => {
            // 若请求已标记为处理过,直接跳过
            if (req.headers['x-rewritten-url'] === 'true') {
              return;
            }

            let url = new URL(req.url, "http://localhost:5173");
            const searchParams = url.searchParams;

            let paramsChanged = false;

            // 检查并修改查询参数
            if (searchParams.has("code")) {
              const value = searchParams.get("code");
              searchParams.delete("code");
              searchParams.append("codeExt", value);
              paramsChanged = true;
            }

            if (searchParams.has("scope")) {
              const value = searchParams.get("scope");
              searchParams.delete("scope");
              searchParams.append("scopeExt", value);
              paramsChanged = true;
            }

            if (searchParams.has("state")) {
              const value = searchParams.get("state");
              searchParams.delete("state");
              searchParams.append("stateExt", value);
              paramsChanged = true;
            }

            if (paramsChanged) {
              const newLocation = url.pathname + "?" + searchParams.toString();

              res.writeHead(302, { // 使用302临时重定向更适配场景
                Location: newLocation,
                "x-rewritten-url": "true",
              });
              console.log("Redirecting to:", newLocation);
              res.end();
            }
          });
        },
      },
    },
  },

方案说明

  • 方案一无需浏览器重定向,性能更优且逻辑简洁,是首选方案。
  • 方案二依赖自定义请求头标记处理状态,需确保浏览器重定向时携带该头,兼容性略逊于方案一。

内容的提问来源于stack exchange,提问作者Maximilian Grinik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:03:14