ASP.NET Core集成测试:UseStatusCodePagesWithReExecute授权测试问题
解决ASP.NET Core集成测试中未授权场景无法触发自定义错误页面的问题
首先明确核心问题:你当前的TestAuthHandler返回AuthenticateResult.Fail属于认证失败(返回401),ASP.NET Core的认证中间件会直接终止管道返回401响应,导致UseStatusCodePagesWithReExecute中间件没有机会处理该状态码。而如果是已认证但未授权(返回403)的场景,授权中间件返回的状态码会被状态码页面中间件正常捕获。
针对两种场景分别给出解决方案:
场景1:测试已认证但未授权(403)
这更符合“用户未授权”的语义,调整TestAuthHandler返回合法的认证结果,但Claims不满足目标Policy的要求:
protected override Task<AuthenticateResult> HandleAuthenticateAsync() { // 构造仅包含基础身份信息的Claims,不满足目标Policy的要求 var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, "test-user-id"), new Claim(ClaimTypes.Name, "Test User") }; var identity = new ClaimsIdentity(claims, "TestAuth"); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, "TestAuth"); return Task.FromResult(AuthenticateResult.Success(ticket)); }
此时访问受Policy保护的接口时,授权中间件会返回403状态码,UseStatusCodePagesWithReExecute会自动触发Home/403对应的HttpStatusCodeHandler方法。
场景2:测试未认证(401)
若确实需要测试未认证场景,需修改认证方案的OnChallenge事件,让它仅设置状态码而非直接终止管道,确保状态码页面中间件能捕获处理:
- 在Startup的认证配置中添加事件处理:
services.AddAuthentication("TestAuth") .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("TestAuth", options => { options.Events = new AuthenticationEvents { OnChallenge = context => { // 设置401状态码,阻止默认的Challenge响应逻辑 context.Response.StatusCode = StatusCodes.Status401Unauthorized; context.HandleResponse(); return Task.CompletedTask; } }; });
- 确保中间件顺序正确(状态码页面中间件需在认证/授权中间件之前注册):
app.UseExceptionHandler("/Home/Error"); // 先注册状态码页面中间件 app.UseStatusCodePagesWithReExecute("/Home/{0}"); // 再注册认证、授权中间件 app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "home_error", pattern: "Home/{statusCode}", defaults: new { controller = "Home", action = "HttpStatusCodeHandler" }, constraints: new { statusCode = new IntRouteConstraint() }); // 其他路由... });
集成测试示例
编写测试用例验证错误页面是否被正确返回:
[Fact] public async Task Unauthenticated_AccessSecurePage_Returns401ErrorPage() { var client = _factory.CreateClient(); var response = await client.GetAsync("/Secure/ProtectedAction"); // 注意:StatusCodePagesWithReExecute会返回200状态码,内容为错误页面 Assert.Equal(HttpStatusCode.OK, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); // 验证页面内容包含预期的错误提示或状态码标识 Assert.Contains("401 Unauthorized", content); }
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

