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

.NET Core自定义ControllerActivator返回非请求控制器引发转换错误求助

自定义ControllerActivator在.NET Core中的转换错误解决方法

我实现了一个自定义ControllerActivator,希望返回与请求目标不同的控制器。这段代码在.NET Framework中能正常运行,但在.NET Core里出现了转换错误,错误调用栈如下:

Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor+SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, object controller, object[] arguments)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()

自定义激活器代码如下:

public object Create(ControllerContext context)
{
    // Always return the TestController, 
    var controller = new TestController();

    MethodInfo method = controller.GetType().GetMethod(context.ActionDescriptor.MethodInfo.Name);

    context.ActionDescriptor.ControllerName = "Test";
    context.ActionDescriptor.DisplayName = context.ActionDescriptor.DisplayName.Replace("HomeController", "TestController");
    context.ActionDescriptor.ControllerTypeInfo = typeof(TestController).GetTypeInfo();
    context.ActionDescriptor.MethodInfo = method;
    controller.ControllerContext = context;

    return controller;
}

问题原因

.NET Core MVC的执行流程中,ActionMethodExecutor是在路由匹配完成后提前根据原始ActionDescriptor创建的,你在控制器激活阶段修改ActionDescriptor的信息已经滞后,导致执行时控制器实例与预先绑定的方法元数据不匹配,触发类型转换错误。

可行实现方案

推荐在路由匹配阶段就替换目标控制器与Action,这符合.NET Core MVC的扩展设计规范,具体步骤如下:

  1. 实现自定义IControllerModelConvention,将所有控制器的Action映射到TestController的对应方法:
public class RedirectToTestControllerConvention : IControllerModelConvention
{
    public void Apply(ControllerModel controller)
    {
        // 跳过TestController本身,避免循环处理
        if (controller.ControllerType == typeof(TestController))
            return;

        // 缓存TestController的公开实例方法,按方法名匹配(忽略大小写)
        var testControllerMethods = typeof(TestController)
            .GetMethods(BindingFlags.Public | BindingFlags.Instance)
            .ToDictionary(m => m.Name, StringComparer.OrdinalIgnoreCase);

        // 遍历当前控制器的所有Action,替换为TestController的对应方法
        foreach (var action in controller.Actions)
        {
            if (testControllerMethods.TryGetValue(action.ActionName, out var targetMethod))
            {
                // 关联到TestController的模型
                action.Controller = controller.Controllers.First(c => c.ControllerType == typeof(TestController));
                // 替换为TestController的目标方法
                action.MethodInfo = targetMethod;
                action.ActionName = targetMethod.Name;
            }
        }
    }
}
  1. 在服务配置中注册该约定:
  • .NET 6+(Program.cs):
builder.Services.AddControllersWithViews(options =>
{
    options.Conventions.Add(new RedirectToTestControllerConvention());
});
  • .NET Core 3.1(Startup.cs):
services.AddControllersWithViews(options =>
{
    options.Conventions.Add(new RedirectToTestControllerConvention());
});
  1. 移除原自定义ControllerActivator,因为路由阶段已直接指向TestController的Action,无需再在激活阶段替换控制器实例。

如果必须保留激活器方式,可通过替换ActionDescriptorCollectionProvider的缓存实现,但这种方式侵入性强,不推荐使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:23:20