ASP.NET Core 8 Web API同步Action Filter转异步出错求助
解决ASP.NET Core 8异步Action Filter的执行错误问题
错误信息
If an IAsyncActionFilter provides a result value by setting the Result property of ActionExecutingContext to a non-null value, then it cannot call the next filter by invoking ActionExecutionDelegate.
问题根源
这个错误的核心是:异步Action Filter中,一旦通过context.Result设置了返回结果(比如未认证、权限不足的响应),就绝对不能再调用await next()执行后续过滤器或Action方法。你的代码里,ValidateUserRoleAsync方法在验证不通过时已经设置了context.Result,但主方法里不管结果如何都会执行await next(),这就触发了框架的校验逻辑。
修正后的完整代码
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; using System; using System.Linq; using System.Threading.Tasks; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public sealed class DemoFilter : ActionFilterAttribute { private readonly RoleEnumType[] _arrRoles; private readonly IUserValidationRepository _userValidationRepository; private readonly ILogger<DemoFilter> _logger; // 构造函数:注入依赖+接收权限参数 public DemoFilter(IUserValidationRepository userValidationRepository, ILogger<DemoFilter> logger, params RoleEnumType[] arrRoles) { _userValidationRepository = userValidationRepository ?? throw new ArgumentNullException(nameof(userValidationRepository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); if (arrRoles == null || !arrRoles.Any()) { throw new ArgumentException("No role passed to check.", nameof(arrRoles)); } _arrRoles = arrRoles; } public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { context.ThrowIfNull(nameof(context)); try { await ValidateUserRoleAsync(context); // 关键:只有当未设置返回结果时,才继续执行后续过滤器/Action if (context.Result == null) { await next(); } } catch (Exception ex) { _logger.LogError(ex, "An error occurred while executing DemoFilter"); throw; } } private async Task ValidateUserRoleAsync(ActionExecutingContext context) { var user = context.HttpContext.User; // 检查用户是否已认证 if (!(user.Identity?.IsAuthenticated).GetValueOrDefault()) { _logger.LogWarning("Unauthorized request: User is not authenticated"); context.Result = new UnauthorizedObjectResult("Unauthorized request: User is not authenticated"); return; } // 从Claims获取用户邮箱和位置ID(根据实际业务调整) string email = user.GetEmail()?.ToLower() ?? throw new InvalidOperationException("User email not found in claims"); string userLocationId = user.FindFirstValue("UserLocationId") ?? throw new InvalidOperationException("User location ID not found in claims"); // 获取用户信息 var userInfo = await _userValidationRepository.GetLoggedInUserDetailsAsync1(email, userLocationId); // 检查用户权限 if (userInfo == null || !_arrRoles.Contains(userInfo.RoleId.ToEnum<RoleEnumType>())) { _logger.LogWarning("User with email {Email} does not have required roles", email); // 权限不足用ForbidResult,语义更准确(未认证用Unauthorized) context.Result = new ForbidResult(); return; } } } // 补充必要的接口定义(根据实际业务调整) public interface IUserValidationRepository { Task<UserInfoRes> GetLoggedInUserDetailsAsync1(string email, string userLocationId); } public class UserInfoRes { public string RoleId { get; set; } // 其他属性 } public enum RoleEnumType { Admin, User, // 其他角色 } // 扩展方法:字符串转枚举(根据实际实现调整) public static class EnumExtensions { public static T ToEnum<T>(this string value) where T : Enum { return (T)Enum.Parse(typeof(T), value, ignoreCase: true); } }
关键修正点
- 构造函数语法修正:原代码中类定义直接带参数的写法不符合C#语法,改为标准构造函数并添加依赖注入支持
- 依赖注入优化:不再从
RequestServices手动获取日志和仓储,改为构造函数注入,符合ASP.NET Core的DI规范 - 执行链控制:验证完成后,只有当
context.Result为null时才调用await next(),彻底避免触发错误 - 返回结果语义修正:权限不足时使用
ForbidResult而非UnauthorizedObjectResult,区分未认证和权限不足的场景 - 空值校验增强:对输入参数、用户Claims等添加严格的空值校验,避免空引用异常
内容的提问来源于stack exchange,提问作者santosh kumar patro
相关产品推荐
相关产品推荐

