.NET Framework 4.8 WebAPI控制器集成测试忽略JwtAuthentication属性问题
问题原因及解决方案
直接调用控制器方法时,Web API的请求处理管道并未启动,标记在方法上的IAuthenticationFilter不会被自动触发。这类过滤器是Web API框架在处理HTTP请求的流程中负责调用的,手动调用方法绕开了整个管道,因此认证逻辑完全被跳过。
解决方案一:手动触发认证过滤器
手动实例化JwtAuthenticationAttribute并调用其AuthenticateAsync方法,传入控制器上下文来执行认证逻辑。同时需要补全过滤器中的异常抛出逻辑,确保测试能捕获到预期异常。
补全过滤器的认证逻辑
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) { var request = context.Request; var authorization = request.Headers.Authorization; if (authorization == null || authorization.Scheme != "Bearer" || string.IsNullOrEmpty(authorization.Parameter)) { throw new JwtAuthenticationException("无效或缺失JWT令牌"); } var token = authorization.Parameter; // 后续令牌验证逻辑... }
修改测试方法
[TestMethod] [ExpectedException(typeof(JwtAuthenticationException))] public async Task GetISOCodes_NoHeader_ThrowException() { // Arrange var authAttribute = new JwtAuthenticationAttribute(); var controllerContext = new HttpControllerContext { RequestContext = new HttpRequestContext(), Request = new HttpRequestMessage() }; languagecontroller.ControllerContext = controllerContext; var authContext = new HttpAuthenticationContext(controllerContext, null); // 手动执行认证逻辑 await authAttribute.AuthenticateAsync(authContext, CancellationToken.None); // 若认证失败,此处已抛出异常 await languagecontroller.GetISOCodes(); } [TestMethod] [ExpectedException(typeof(JwtAuthenticationException))] public async Task GetISOCodes_WithHeader_ThrowException() { // Arrange var token = string.Empty; var authAttribute = new JwtAuthenticationAttribute(); var controllerContext = new HttpControllerContext { RequestContext = new HttpRequestContext(), Request = new HttpRequestMessage() }; controllerContext.Request.Headers.Add("Authorization", "Bearer " + token); languagecontroller.ControllerContext = controllerContext; var authContext = new HttpAuthenticationContext(controllerContext, null); // 手动执行认证逻辑 await authAttribute.AuthenticateAsync(authContext, CancellationToken.None); // 若认证失败,此处已抛出异常 await languagecontroller.GetISOCodes(); }
解决方案二:使用Web API测试服务器(推荐)
通过HttpServer和HttpClient模拟真实HTTP请求,让Web API完整的请求管道运行,过滤器会自动触发,测试场景更贴近真实环境。
测试代码示例
[TestMethod] public async Task GetISOCodes_NoHeader_ReturnsUnauthorized() { // 配置Web API var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}", defaults: new { action = RouteParameter.Optional } ); using (var server = new HttpServer(config)) using (var client = new HttpClient(server)) { // Act var response = await client.GetAsync("api/Language/GetISOCodes"); // Assert Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); // 若过滤器抛出异常,可读取错误信息 var error = await response.Content.ReadAsAsync<HttpError>(); Assert.AreEqual("无效或缺失JWT令牌", error.Message); } } [TestMethod] public async Task GetISOCodes_EmptyToken_ReturnsUnauthorized() { var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}", defaults: new { action = RouteParameter.Optional } ); using (var server = new HttpServer(config)) using (var client = new HttpClient(server)) { // Arrange client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", string.Empty); // Act var response = await client.GetAsync("api/Language/GetISOCodes"); // Assert Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); var error = await response.Content.ReadAsAsync<HttpError>(); Assert.AreEqual("无效或缺失JWT令牌", error.Message); } }
注意事项
如果过滤器中使用context.ErrorResult而非直接抛出异常,测试时应验证响应的状态码和错误内容,而非依赖ExpectedException特性。
内容的提问来源于stack exchange,提问作者Drahcir
相关产品推荐
相关产品推荐

