You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 04:27:34