太好了!這是最有價值的一條線索!這則錯誤訊息非常精準。 "您必須在要求本文中傳遞查詢物件" (You must pass a query object in the request body) 核心診斷 這表示您所使用的 Azure DevOps Server 版本,在處理 POST .../_apis/wit/wiql/{queryId} 這個 API 時的行為與預期不同。 * 我們的預期:我們提供一個已儲存查詢的 ID,伺服器直接執行它 (請求的 Body 為空)。 * 伺服器的要求:它不接受空的 Body,它要求我們必須在 Body 中提供 WIQL (Work Item Query Language) 查詢語法字串。 這不是您的錯,很可能是該伺服器版本的一個特性或 Bug。 解決方案 (釜底抽薪) 既然伺服器這麼要求,我們就照做。我們會調整邏輯,分成兩步來執行查詢: * 第一步:取得查詢語法 - 我們先用您的 queryId 去請求 API,取得這個 ID 對應的 WIQL 查詢語法文字。 * 第二步:執行查詢語法 - 我們再把拿到的語法文字放到請求的 Body 中,發送到另一個 API 端點去執行。 這個方法繞過了有問題的 API 行為,直接滿足伺服器的要求。 精準的程式碼更新 請用以下程式碼 完整取代 您現有的 GetWorkItemIdsAsync 函式。您還需要在檔案最下方加入兩個新的 Class。 其他函式 (Main, RunAsync, GetWorkItemDetailsAsync 等) 完全不需要 變動。 ' ... (檔案頂端的 Imports 和 Module1 的設定變數都保持不變) ' ========================================================================= ' ▼▼▼▼▼▼▼▼▼▼▼▼ 請只用這段新程式碼取代舊的 GetWorkItemIdsAsync 函式 ▼▼▼▼▼▼▼▼▼▼▼▼ ' ========================================================================= Private Async Function GetWorkItemIdsAsync() As Task(Of List(Of WorkItemReference)) ' --- 步驟 1: 先用 Query ID 取得查詢的詳細定義 (包含 WIQL 語法) --- Dim getQueryDefinitionUrl = $"{organizationUrl}/{projectName}/_apis/wit/queries/{queryId}?api-version=7.0" Console.WriteLine($"[DEBUG] 正在 GET 請求查詢定義 URL: {getQueryDefinitionUrl}") Dim wiqlString As String Using response = Await httpClient.GetAsync(getQueryDefinitionUrl) Dim responseBody = Await response.Content.ReadAsStringAsync() If Not response.IsSuccessStatusCode Then Console.ForegroundColor = ConsoleColor.Red Console.WriteLine($"[步驟1失敗] 取得查詢定義失敗! 狀態碼: {response.StatusCode}") Console.WriteLine("==== 伺服器回應內容 ====") Console.WriteLine(responseBody) Console.WriteLine("========================") Throw New Exception("取得查詢定義失敗。請確認 Query ID 是否正確。") End If Dim queryDefinition = JsonConvert.DeserializeObject(Of QueryDefinition)(responseBody) wiqlString = queryDefinition.wiql Console.WriteLine($"取得 WIQL 成功: ""{wiqlString}""") End Using ' --- 步驟 2: 將取得的 WIQL 語法放到 Body 中,去執行查詢 --- Dim executeQueryUrl = $"{organizationUrl}/{projectName}/_apis/wit/wiql?api-version=7.0" Console.WriteLine($"[DEBUG] 正在 POST 請求執行查詢 URL: {executeQueryUrl}") ' 建立伺服器要求的請求 Body 物件 Dim queryBody As New AdHocQueryBody With { .query = wiqlString } Dim jsonBody = JsonConvert.SerializeObject(queryBody) Dim requestContent = New StringContent(jsonBody, Encoding.UTF8, "application/json") Using response = Await httpClient.PostAsync(executeQueryUrl, requestContent) Dim responseBody = Await response.Content.ReadAsStringAsync() If Not response.IsSuccessStatusCode Then Console.ForegroundColor = ConsoleColor.Red Console.WriteLine($"[步驟2失敗] 執行查詢失敗! 狀態碼: {response.StatusCode}") Console.WriteLine("==== 伺服器回應內容 ====") Console.WriteLine(responseBody) Console.WriteLine("========================") Throw New Exception("執行 WIQL 查詢失敗。") End If Dim result = JsonConvert.DeserializeObject(Of QueryResult)(responseBody) Return result.workItems End Using End Function ' ========================================================================= ' ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ ' ========================================================================= ' (Module1 中其他的程式碼,如 GetWorkItemDetailsAsync 等,都保持不變) End Module ' --- 以下是用於解析 JSON 回應的資料模型 --- ' (QueryResult, WorkItemReference ... 等舊的 Class 保持不變) '... ' ========================================================================= ' ▼▼▼▼▼▼▼▼▼▼▼▼▼▼ 請在檔案最下方,加入這兩個新的 Class ▼▼▼▼▼▼▼▼▼▼▼▼▼ ' ========================================================================= ' 用於解析查詢定義的 JSON 模型 Public Class QueryDefinition Public Property wiql As String End Class ' 用於建立執行查詢時的請求 Body 的 JSON 模型 Public Class AdHocQueryBody Public Property query As String End Class 執行步驟 * 找到 GetWorkItemIdsAsync 函式,用上面提供的新版本完整取代它。 * 捲到 .vb 檔案的最下面,在所有現有的 Class 定義之後,貼上 QueryDefinition 和 AdHocQueryBody 這兩個新的 Class。 * 重新建置 (Build) 並執行。 這次的修改是完全針對伺服器回傳的錯誤訊息來設計的,直接提供了它所要求的資料,成功率應該非常高。
天啊,這太棒了!您找到了最關鍵的**「冒煙的槍」(Smoking Gun)**!
















