【问题标题】:Replace a controller action at runtime在运行时替换控制器动作
【发布时间】:2012-05-04 17:40:45
【问题描述】:

我们部署了一个 ASP.NET MVC 3 应用程序,并在其中一个控制器操作中发现了一个错误。出于政治原因,我们不允许在没有需要数周时间的大流程的情况下替换任何现有代码。 IE。我们无法重建 MVC 应用程序。

我们可以部署一个新的程序集,其中包含一个只有一个固定操作的新控制器。

有没有办法添加或编辑 MVC 应用程序的路由以映射新的控制器操作?


我正在考虑在修补程序 DLL 中子类化我的 MVC 应用程序并更新 global.asax 以引用子类化应用程序。

public class HotfixApplication : RealApplication
{
    public override void Init()
    {
        base.Init();
        var badRoute =  RouteTable.Routes.Where(...);
        var badRoute.FixIt();
    }
}

它会路由到修补程序 DLL 中的控制器操作。

这听起来合理吗?安全吗?

【问题讨论】:

  • 是的,但这需要在旧路由之前将您的新路由添加到路由集合中,或者如果您使用 UrlRewriter.net 之类的东西,您可以尝试重写路由。否则,您需要硬着头皮部署修复程序。向您的业务人员解释情况。这是一个网站,而不是客户端应用程序。
  • 另外,您必须告诉控制器工厂在哪里可以找到控制器。
  • 你能摆脱疯狂的政治限制吗?如果发现安全漏洞会怎样?

标签: asp.net-mvc asp.net-mvc-3


【解决方案1】:

试试这个基于ControllerFactory、HttpModule的方法:

步骤:

1# 创建一个新的“类库”项目。添加对您的 asp.net mvc Web 应用程序项目的引用。还添加对“System.Web”、“System.Web.Mvc”、“Microsoft.Web.Infrastructure.dll”程序集的引用。

2#新建一个继承自有bug的控制器(TargetController)的Controller类:

public class FixedContorller : TargetController
{

    [FixedActionSelector]
    [ActionName("Index")]
    public ActionResult FixedIndex()
    {
        ViewBag.Title = "I'm Fixed...";
        return View();
    }
}

[FixedActionSelectorAttribute]是ActionSelector属性,用于解析动作名称。

    public class FixedActionSelector : ActionMethodSelectorAttribute
    {
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        return true;
    }
}

3# 定义一个自定义ControllerFactory,它将创建固定控制器而不是目标控制器:

public class MyControllerFactory : DefaultControllerFactory
{
    private static string targetControllerName = "Target";
    private static string targetActionName = "Index";

    protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        var action = requestContext.RouteData.Values["action"].ToString();

        if (targetControllerName.Equals(controllerName, StringComparison.InvariantCultureIgnoreCase) &&
            targetActionName.Equals(action, StringComparison.InvariantCultureIgnoreCase))
        {
            return typeof(FixedContorller);
        }

        return base.GetControllerType(requestContext, controllerName);
    }
}

4# 现在定义一个 HttpModule,它将在应用程序初始化时设置上述控制器工厂。 httpModule 将以编程方式注册自己,无需在 web.config 中注册。

using System;
using System.Web;
using System.Web.Mvc;

[assembly: PreApplicationStartMethod(typeof(YourApp.Patch.FixerModule), "Register")]
namespace YourApp.Patch
{
    public class FixerModule : IHttpModule
    {
        private static bool isStarted = false;
        private static object locker = new object();

        public void Dispose()
        {
        }

        public void Init(HttpApplication context)
        {
            if (!isStarted)
            {
                lock (locker)
                {
                    if (!isStarted)
                    {
                        ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
                        isStarted = true;
                    }
                }
            }
        }

        public static void Register()
        {
            Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(FixerModule));
        }
    }
}

编译项目并将 .dll 复制到 Web 应用程序的 bin 文件夹中。

希望这会有所帮助...

【讨论】:

  • 你能发布一个 github 解决方案/示例并在其中工作吗?
【解决方案2】:

如果我错了,请有人纠正我,但我很确定,除非您的项目被设计为动态加载程序集,否则您将无法在没有某种部署的情况下修改您的运行时。

话虽如此,您可以利用 ISS 重定向或添加到 Web.config 文件中的 HttpModule 将请求指向有问题的控制器的请求指向您选择的新位置。

【讨论】:

  • ASP.NET 似乎加载了 bin 目录中的任何程序集。并且控制器工厂可以提取后期绑定的控制器;我只是在纠结如何更新路由表。
猜你喜欢
  • 2013-07-03
  • 1970-01-01
  • 2012-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-21
  • 1970-01-01
相关资源
最近更新 更多