如何在FastEndpoints的AppFixture中正确删除Cookie?
问题背景
我用FastEndpoints结合Cookie认证开发REST API,登录逻辑如下,在Swagger和AppFixture集成测试里都能正常工作,客户端可自动处理Cookie:
await HttpContext.SignInAsync(await HttpContext.GetCookieAuthenticationSchemeAsync(), CreatePrincipal(), new AuthenticationProperties());
但登出功能仅在Swagger中有效,AppFixture测试里完全失效:
if (User.Identity?.IsAuthenticated == true) { await httpContext.SignOutAsync(); }
尝试手动删除Cookie也没用,能看到Cookie被标记为删除,但AppFixture就是不识别:
var siteCookies = HttpContext.Request.Cookies; foreach (var cookie in siteCookies) { httpContext.Response.Cookies.Delete(cookie.Key); // 或者 var opts = new CookieOptions(); opts.Expires = DateTimeOffset.MinValue; httpContext.Response.Cookies.Append(cookie.Key, string.Empty, opts); }
后来自定义了MyApiFixture处理Cookie,虽然能看到登出后的删除/过期操作已应用,但测试时还是会发送之前的有效Cookie:
public class MyApiFixture : AppFixture<Program> { public void CreateMyClient() { var options = new ClientOptions { HandleCookies = false, // 手动处理Cookie }; options.AddHandlers(new CookieContainerHandler(CookieJar)); Client = CreateClient(options); } public CookieContainer CookieJar { get; } = new(); public void UpdateCookies(HttpResponseMessage response) { if (!response.Headers.Contains("Set-Cookie")) return; var uri = new Uri("http://localhost"); foreach (var cookieHeader in response.Headers.GetValues("Set-Cookie")) { CookieJar.SetCookies(uri, cookieHeader); } } }
核心原因
CookieContainer处理Set-Cookie头的删除/过期指令时,要求完全匹配原始Cookie的所有属性(比如Path、Domain、SameSite等),否则不会移除旧Cookie。FastEndpoints默认客户端的Cookie处理逻辑没有完整传递这些属性,导致删除指令不生效。
修复方案
1. 登出时指定正确的认证方案
确保登出用的认证方案和登录一致,避免默认方案不匹配:
if (User.Identity?.IsAuthenticated == true) { var scheme = await HttpContext.GetCookieAuthenticationSchemeAsync(); await HttpContext.SignOutAsync(scheme); }
2. 完善Fixture的Cookie处理逻辑
修改UpdateCookies方法,使用请求的实际URI,并且手动解析Cookie属性确保完全匹配:
public void UpdateCookies(HttpResponseMessage response, Uri requestUri) { if (!response.Headers.Contains("Set-Cookie")) return; foreach (var cookieHeader in response.Headers.GetValues("Set-Cookie")) { var cookieCollection = new CookieCollection(); cookieCollection.SetCookies(requestUri, cookieHeader); foreach (Cookie cookie in cookieCollection) { if (cookie.Expires <= DateTime.Now) { // 发送明确的删除指令,匹配原始Cookie的Path属性 CookieJar.SetCookies(requestUri, $"{cookie.Name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path={cookie.Path}"); } else { CookieJar.Add(requestUri, cookie); } } } }
3. 测试时传递正确的请求URI
调用UpdateCookies时,传入当前请求的完整URI,不要硬编码固定值:
// 登录并更新Cookie var loginResponse = await Client.PostAsJsonAsync("/auth/login", loginRequest); fixture.UpdateCookies(loginResponse, loginResponse.RequestMessage.RequestUri); // 登出并更新Cookie var logoutResponse = await Client.PostAsync("/auth/logout", null); fixture.UpdateCookies(logoutResponse, logoutResponse.RequestMessage.RequestUri);
4. 验证Cookie状态(可选)
测试里可以手动检查CookieContainer,确认旧Cookie已被移除:
var cookies = fixture.CookieJar.GetCookies(new Uri("http://localhost")); Assert.DoesNotContain(cookies, c => c.Name == ".AspNetCore.Identity.Application");
内容的提问来源于stack exchange,提问作者S. Schulz
相关产品推荐
相关产品推荐

