.NET C#测试中POST请求无法命中带授权的控制器方法问题
问题分析与解决方案
你的核心问题是测试代码中两次请求使用了独立的HttpClient实例,导致登录后的会话凭证(Cookie/Token)无法共享,进而触发[Authorize]拦截,请求未进入目标方法逻辑就返回了200(可能是授权失败后的跳转或默认响应)。以下是具体修复方案:
关键修复点
1. 复用同一个HttpClient实例
HttpClient设计为可复用对象,每次创建新实例会重置会话上下文,导致登录状态丢失。将整个测试流程放在同一个HttpClient的using块中。
2. 修正请求发送方式
- 登录接口如果接收表单数据,需用
PostAsync+FormUrlEncodedContent,而非PostAsJsonAsync(后者会把表单内容序列化为JSON,不符合接口预期)。 - 目标接口的参数传递需匹配控制器方法的绑定规则:
id未加修饰符时,ASP.NET Core默认从表单或请求体绑定,需保证请求格式正确。
3. 避免async void的测试方法
测试方法使用async void会导致测试框架无法正确等待异步操作完成,改为async Task。
修复后的测试代码(Cookie认证场景)
[Fact] public async Task Modelo761_001() { // 复用同一个HttpClient,保留会话凭证 using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.BaseAddress = new Uri("https://localhost:7117/"); // 设置基础地址,简化后续路径 #region 登录获取会话 var loginUrl = "Login/Acceder"; var loginParams = new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("UserName","XXX"), new KeyValuePair<string, string>("Password","XXX") }; var loginContent = new FormUrlEncodedContent(loginParams); // 发送表单格式的登录请求 HttpResponseMessage loginResponse = await client.PostAsync(loginUrl, loginContent); loginResponse.EnsureSuccessStatusCode(); // 确保登录成功,否则抛出异常 #endregion #region 调用带授权的目标接口 try { var ejecutarModeloUrl = "Pools/Management/EjecutarModeloProgramado?anyo=2023&mes=6"; var modeloParams = new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("id","4b4516a4-c0a5-493e-9bd1-08dbe9c20c33") }; var modeloContent = new FormUrlEncodedContent(modeloParams); // 复用登录后的HttpClient发送请求 HttpResponseMessage response = await client.PostAsync(ejecutarModeloUrl, modeloContent); response.EnsureSuccessStatusCode(); // 可选:读取响应内容验证结果 var responseContent = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseContent); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } #endregion } }
若使用JWT认证场景的适配
如果你的系统用JWT Token认证,需从登录响应中提取Token并添加到请求头:
[Fact] public async Task Modelo761_001() { using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.BaseAddress = new Uri("https://localhost:7117/"); #region 登录获取JWT Token var loginUrl = "Login/Acceder"; var loginParams = new { UserName = "XXX", Password = "XXX" }; HttpResponseMessage loginResponse = await client.PostAsJsonAsync(loginUrl, loginParams); loginResponse.EnsureSuccessStatusCode(); // 从响应中解析Token(根据实际响应结构调整) var loginResult = await loginResponse.Content.ReadFromJsonAsync<dynamic>(); string token = loginResult.token; // 添加Authorization头到HttpClient默认配置 client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); #endregion #region 调用目标接口 try { var ejecutarModeloUrl = "Pools/Management/EjecutarModeloProgramado?anyo=2023&mes=6"; // 若id是[FromBody]绑定,用JSON格式传递 var modeloRequest = new { id = Guid.Parse("4b4516a4-c0a5-493e-9bd1-08dbe9c20c33") }; HttpResponseMessage response = await client.PostAsJsonAsync(ejecutarModeloUrl, modeloRequest); response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseContent); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } #endregion } }
额外注意事项
- 确保登录用户的角色属于
Admin, Alto, Medio中的某一个,否则会返回403 Forbidden。 - 控制器方法中
Mediator.Send(...).Result会阻塞线程,建议改为await Mediator.Send(...)避免死锁风险。
内容的提问来源于stack exchange,提问作者Diego Perez
相关产品推荐
相关产品推荐

