.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
相关产品推荐
相关产品推荐

