ASP.NET Core 6中Autofac/Castle DynamicProxy拦截器失效排查
ASP.NET Core 6中Autofac拦截器未执行的问题排查与解决
问题描述
实现ASP.NET Core 6服务时,使用Autofac配置控制器拦截器,但拦截器的Intercept方法从未触发,断点和日志均无反应。相关配置代码如下:
Program.cs中的DI配置
using Autofac; using Autofac.Extensions.DependencyInjection; using Autofac.Extras.DynamicProxy; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; internal class Program { private static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddRequestHandlers(); builder.Services.AddConfigurationRepository(builder.Configuration); builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureContainer<ContainerBuilder>(autofacBuilder => { autofacBuilder.RegisterType<ControllerExceptionHandlingInterceptor>(); autofacBuilder.RegisterType<MyController>().SingleInstance() .EnableClassInterceptors() .InterceptedBy(typeof(ControllerExceptionHandlingInterceptor)); }); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); } }
拦截器实现
using System.Threading.Tasks; using System; using Castle.DynamicProxy; using IInterceptor = Castle.DynamicProxy.IInterceptor; using IActionResult = Microsoft.AspNetCore.Mvc.IActionResult; using Microsoft.Extensions.Logging; using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Http; using System.Reflection; namespace Wattle.Merchant.Configuration.Host { public class ControllerExceptionHandlingInterceptor : IInterceptor { private readonly ILogger<ControllerExceptionHandlingInterceptor> _logger; public ControllerExceptionHandlingInterceptor( ILogger<ControllerExceptionHandlingInterceptor> logger) { _logger = logger; } public void Intercept(IInvocation invocation) { var methodInfo = invocation.MethodInvocationTarget; if (IsAsync(methodInfo)) { invocation.Proceed(); invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue, invocation); } else { InterceptSync(invocation); } } private async Task<TResult> InterceptAsync<TResult>(Task<TResult> task, IInvocation invocation) { try { return await task.ConfigureAwait(false); } catch (Exception ex) { return (TResult)HandleException(invocation, ex); } } private void InterceptSync(IInvocation invocation) { try { invocation.Proceed(); } catch (Exception ex) { invocation.ReturnValue = HandleException(invocation, ex); } } private IActionResult HandleException(IInvocation invocation, Exception ex) { _logger.LogError(ex, $"{invocation.Method.Name} failed."); if (ex.GetType().IsAssignableFrom(typeof(ValidationException))) return StatusCode(StatusCodes.Status400BadRequest, ex.Message); if (ex.GetType().IsAssignableFrom(typeof(NotFoundException))) return StatusCode(StatusCodes.Status404NotFound, ex.Message); return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); } public static bool IsAsync(MethodInfo method) { if (method.ReturnType == typeof(Task)) return true; if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) return true; return false; } private static ObjectResult StatusCode( [ActionResultStatusCode] int statusCode, [ActionResultObjectValue] object? value) { return new ObjectResult(value) { StatusCode = statusCode }; } } }
问题根源
核心问题是ASP.NET Core默认会自动实例化控制器,不会从Autofac容器中获取你注册的带拦截器的实例。builder.Services.AddControllers()已经自动将所有控制器注册到DI容器,你手动在Autofac中注册的MyController不会覆盖默认注册,导致框架使用的是未启用拦截的控制器实例。
此外,控制器使用SingleInstance()生命周期是错误的——控制器应与请求周期绑定(Scoped),单例会引发依赖注入生命周期不匹配的问题。
解决步骤
1. 配置框架从DI容器解析控制器
修改AddControllers()的配置,添加AddControllersAsServices(),让ASP.NET Core从DI容器中获取控制器实例:
builder.Services.AddControllers() .AddControllersAsServices(); // 关键:启用控制器的DI解析模式
2. 调整Autofac注册逻辑
方式一:批量注册所有控制器并应用拦截器(推荐)
autofacBuilder.RegisterType<ControllerExceptionHandlingInterceptor>().SingleInstance(); // 批量注册当前程序集中的所有控制器 autofacBuilder.RegisterAssemblyTypes(typeof(Program).Assembly) .Where(t => t.IsAssignableTo<ControllerBase>()) .InstancePerLifetimeScope() // 匹配控制器默认的Scoped生命周期 .EnableClassInterceptors() .InterceptedBy(typeof(ControllerExceptionHandlingInterceptor));
方式二:单独注册特定控制器
如果只需要拦截单个控制器,将生命周期改为InstancePerLifetimeScope并确保注册优先级:
autofacBuilder.RegisterType<ControllerExceptionHandlingInterceptor>().SingleInstance(); autofacBuilder.RegisterType<MyController>() .InstancePerLifetimeScope() .EnableClassInterceptors() .InterceptedBy(typeof(ControllerExceptionHandlingInterceptor)) .AsSelf(); // 明确注册为自身类型,覆盖默认注册
3. 确保Action方法支持类拦截
使用EnableClassInterceptors()时,Autofac基于动态代理创建控制器子类,被拦截的Action方法必须是虚方法。如果不想修改Action方法,可以改用接口拦截:
- 定义包含Action方法的接口,让控制器实现该接口
- 将
EnableClassInterceptors()替换为EnableInterfaceInterceptors()
额外优化点
- 拦截器依赖
ILogger(Singleton生命周期),可以将拦截器注册为SingleInstance,减少实例创建开销 - 避免给控制器使用
SingleInstance,否则会导致Scoped/Transient依赖注入异常
内容的提问来源于stack exchange,提问作者balintn
相关产品推荐
相关产品推荐

