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

.NET Core 2.2 MVC API版本控制中间件执行时机问题

解决方案:在.NET Core 2.2 MVC中针对特定API版本运行自定义逻辑

这个问题我之前也碰到过,核心矛盾点在于:UseMvc属于终端中间件,放在它之后的中间件根本不会被触发;但API版本信息默认是在MVC管道内部完成解析的,提前注册中间件又拿不到版本数据。这里有两种针对性的解决方案,你可以根据场景选择:

方案一:调整中间件位置,手动解析API版本

既然中间件必须放在UseMvc之前,那我们可以主动调用IApiVersionReader来读取版本信息,而不是依赖MVC后续填充的数据。

步骤1:调整中间件注册顺序

修改Startup.Configure方法,把你的自定义中间件移到UseMvc之前:

public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
    // 移到UseMvc之前,确保中间件能被触发
    app.UseMiddleware<UserDataLoggingMiddleware>();
    app.UseMvc();
}

步骤2:修改中间件实现,手动读取版本

在中间件中注入IApiVersionReader和API版本配置选项,手动读取并判断版本:

public class UserDataLoggingMiddleware {
    private readonly RequestDelegate _next;
    private readonly IApiVersionReader _versionReader;
    private readonly ApiVersion _defaultApiVersion;

    // 通过构造函数注入所需服务
    public UserDataLoggingMiddleware(
        RequestDelegate next, 
        IApiVersionReader versionReader, 
        IOptions<ApiVersioningOptions> apiVersioningOptions) {
        _next = next;
        _versionReader = versionReader;
        _defaultApiVersion = apiVersioningOptions.Value.DefaultApiVersion;
    }

    public async Task InvokeAsync(HttpContext context) {
        // 手动读取请求中的API版本,没有则使用默认版本
        var requestedVersion = _versionReader.Read(context) ?? _defaultApiVersion;
        
        // 仅在v2及以上版本执行你的逻辑
        if (requestedVersion >= new ApiVersion(2, 0)) {
            await LogUserData(context);
        }

        // 继续执行后续管道
        await _next(context);
    }

    // 你的自定义日志逻辑
    private async Task LogUserData(HttpContext context) {
        // 这里实现你的用户数据记录逻辑
    }
}

方案二:使用MVC动作过滤器(更推荐)

如果你的逻辑只需要针对API控制器/动作执行,用动作过滤器会更省心——过滤器运行在MVC管道内部,此时API版本已经被解析完成,直接就能获取到版本信息,不用手动处理。

步骤1:创建自定义动作过滤器

public class UserDataLoggingFilter : IActionFilter {
    // 在动作执行前触发
    public void OnActionExecuting(ActionExecutingContext context) {
        // 直接从HttpContext获取已解析的API版本
        if (context.HttpContext.GetRequestedApiVersion() is ApiVersion version && version >= new ApiVersion(2, 0)) {
            LogUserData(context.HttpContext);
        }
    }

    // 动作执行后触发(可选)
    public void OnActionExecuted(ActionExecutedContext context) {
        // 可以在这里处理动作执行后的逻辑,比如记录响应数据
    }

    // 你的自定义日志逻辑
    private void LogUserData(HttpContext context) {
        // 实现用户数据记录逻辑
    }
}

步骤2:注册过滤器到MVC管道

在Startup.ConfigureServices的AddMvc配置中添加这个过滤器:

public void ConfigureServices(IServiceCollection services) {
    services.AddMvc(config => {
        // Don't add auth in development.
        if (!CurrentEnvironment.IsDevelopment()) {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            config.Filters.Add(new AuthorizeFilter(policy));
        }
        // 注册自定义过滤器,全局生效
        config.Filters.Add<UserDataLoggingFilter>();
    });
    
    // 其他服务注册代码...
    services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
    services.AddMvcCore()
        .AddAuthorization()
        .AddJsonFormatters(config => config.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
    services.AddApiVersioning(options => {
        options.ReportApiVersions = true;
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.DefaultApiVersion = new ApiVersion(2, 0);
    });
}

如果不想全局生效,还可以用[TypeFilter(typeof(UserDataLoggingFilter))]特性,只标记在v2及以上的控制器/动作方法上,控制更精准。

方案对比

  • 中间件方案:适合需要对所有请求(包括非MVC请求)执行逻辑的场景,但需要手动处理版本解析。
  • 动作过滤器方案:更贴合MVC/API场景,无需手动解析版本,利用MVC已有的版本解析结果,配置更灵活,是更推荐的方案。

内容的提问来源于stack exchange,提问作者David C

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:01:22