Vite dev server: proxy config normalization reorders glob keys, drops string `context` and misses URLs with a query string
維護者通常 1 天內回覆
已經有一個關聯 PR 被合併了。
- #34258 來自 @aminesbdev —— 已合併
評估
- 難度
- 4/5
- 預估耗時
- 3-5 天
- 新手友好度
- 65/100
- Issue 類型
- 缺陷
- 描述清晰度
- 描述清楚
- 活躍度
- 活躍
- 技術堆疊
- angular, node.js, typescript, vite
研究方向
Start with packages/angular/build/src/utils/load-proxy-config.ts and inspect normalizeProxyConfiguration, then trace how Vite matches proxy patterns against request URLs. Check existing proxy configuration tests and run the relevant tests for ordering, string context, and query strings. Done means all three reproductions proxy to the expected backend without changing unrelated matching behavior.
由索引模型根據 Issue 內容生成。
描述
Command
serve
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
The webpack-based @angular-devkit/build-angular:dev-server
Description
normalizeProxyConfiguration in packages/angular/build/src/utils/load-proxy-config.ts turns the proxy file into the object Vite expects. Three things get lost along the way. In each case the request isn't proxied and falls through to the app (index.html), and nothing shows up in the ng serve output.
- Glob entries lose their position. Each glob key is replaced with
normalizedProxy[pattern] = …; delete normalizedProxy[key], which moves it to the end of the object. Vite uses the first entry that matches, so a specific glob written first loses to a broader plain key written after it. - A string
contextdrops the whole entry. In the array form, an entry whosecontextisn't an array is skipped. webpack-dev-server passescontextto http-proxy-middleware aspathFilter, which accepts a single string, socontext: '/legacy'works there. The array form conversion was added after #25337 so that webpack-style configs keep working, and this case slipped through. - Globs are matched against the URL including its query string. The regex built by picomatch ends with
$and Vite tests it againstreq.url, so/search/**matches/searchbut not/search?q=angular. http-proxy-middleware matches both globs and plain keys against the pathname only.
Minimal Reproduction
A new app served by @angular/build:dev-server, plus a backend that echoes the port and URL it receives:
// backend.mjs
import http from 'node:http';
for (const port of [3998, 3999]) {
http.createServer((req, res) => res.end(`${port} got ${req.url}\n`)).listen(port);
}
1. Order with ng serve --proxy-config proxy.conf.json:
{
"/api/auth/**": {"target": "http://localhost:3998"},
"/api/": {"target": "http://localhost:3999"}
}
curl localhost:4200/api/auth/login returns 3999 got /api/auth/login. I expected it to go to 3998.
2. String context with ng serve --proxy-config proxy.conf.mjs:
export default [{context: '/legacy', target: 'http://localhost:3999'}];
curl localhost:4200/legacy/users returns index.html. loadProxyConfiguration returns {} for this file.
3. Query string with ng serve --proxy-config proxy.conf.json:
{
"/search/**": {"target": "http://localhost:3999"}
}
curl localhost:4200/search is proxied, but curl 'localhost:4200/search?q=angular' returns index.html.
Exception or Error
None. The requests are just not proxied.
Your Environment
Angular CLI : 22.2.0
Angular : 22.2.0
Node.js : 24.21.0
Package Manager : npm 11.19.0
Operating System : darwin arm64
@angular/build : 22.2.0
@angular/cli : 22.2.0
vite : 8.3.0
The code is the same on main.
Anything else relevant?
#34258 fixes the first two points. The third one isn't part of it: for a trailing /**, picomatch puts a $ inside the generated regex, so it takes more than appending an optional query string, and it changes which requests get proxied.
- 主要語言
- TypeScript
- 星號
- 27k
- 分支
- 11.8k
- 平均合併
- 1 天 12 分鐘
- 30 天內合併 PR
- 171
環境準備
- 沒有 Dockerfile 或 Docker Compose 檔案
- 有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
angular/angular-cli 的其他 Issue
-
area: @angular/build gemini-triaged
難度 2/5 1-3 小時 新手友好度 74/100
angular/angular-cli#33955 ·
維護者通常 1 天內回覆
-
area: @angular/cli gemini-triaged
難度 2/5 1-3 小時 新手友好度 72/100
angular/angular-cli#33055 · 1 則留言 · 3 個 reaction ·
維護者通常 1 天內回覆
-
area: @angular/build
難度 4/5 3-5 天 新手友好度 25/100
angular/angular-cli#34292 ·
維護者通常 1 天內回覆
-
dev-server: es2016 prebundle target for zone.js apps lowers private fields and breaks dependencies未關閉area: @angular/build
難度 4/5 3-5 天 新手友好度 48/100
angular/angular-cli#34280 ·
維護者通常 1 天內回覆
-
area: @angular/build
難度 4/5 3-5 天 新手友好度 48/100
angular/angular-cli#34262 ·
維護者通常 1 天內回覆
查看 angular/angular-cli 的全部 Issue
相似的 Issue
-
難度 1/5 1-3 小時 新手友好度 84/100
維護者通常 1 天內回覆
-
core
難度 2/5 1-3 小時 新手友好度 70/100
vectorize-io/hindsight#5457 ·
維護者通常 1 天內回覆
-
beginner friendly community contributions-welcome good first issue hacktoberfest help wanted testing up-for-grabs
難度 2/5 1-3 小時 新手友好度 72/100
維護者通常 1 天內回覆
-
難度 1/5 1 小時以內 新手友好度 85/100
lukilabs/beautiful-mermaid#160 ·
-
難度 2/5 1-3 小時 新手友好度 66/100
rescript-lang/rescript-lang.org#1420 ·
維護者通常 2 天內回覆