Replace controller action at run time

We have a deployed ASP.NET MVC 3 application and have detected an error in one of the controller actions. For political reasons, we are not allowed to replace any existing code without a large process that takes several weeks of headache. That is, we cannot rebuild the MVC application.

We are allowed to deploy a new assembly containing a new controller with only one fixed action.

Is there a way to add or change MVC application routes to map a new controller action?


I am considering subclassing my MVC application into a patch DLL and updating global.asax to reference a subclassed application.

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

And it will be redirected to the action of the controller in the patch DLL file.

Does this sound believable? Safely?

+6
source share
2 answers

Try this approach based on ControllerFactory, HttpModule:

Steps:

1 # Create a new class library project. Add a link to your asp.net mvc web application project. Also add a link to the assemblies "System.Web", "System.Web.Mvc", "Microsoft.Web.Infrastructure.dll".

2 # Create a new controller class inherited from the controller (TargetController), which has an error:

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

[FixedActionSelectorAttribute] is an ActionSelector attribute used to resolve the name of an action.

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

3 # Define a custom ControllerFactory that will create a fixed controller instead of the target controller:

 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 # Now define an HttpModule that will install the above controller - factory in the init application. HttpModule will be registered programmatically, no need to register in 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)); } } } 

Compile the project and copy the DLL to the bin folder of your web application.

Hope this helps ...

+3
source

Someone correct me if I am wrong, but I am sure that if your project is not designed to dynamically load assemblies, you will not be able to change your working environment without any deployment.

In doing so, you can use the ISS or HttpModule redirects added to your Web.config file to point requests to the intruder controller to a new location of your choice.

0
source

Source: https://habr.com/ru/post/914920/


All Articles