How to achieve a dynamic controller and action method in ASP.NET MVC?

前端 未结 3 1973
遇见更好的自我
遇见更好的自我 2020-12-02 14:26

In Asp.net MVC the url structure goes like

http://example.com/{controller}/{action}/{id}

For each \"controller\", say http://example.com/blog, there is a Bl

相关标签:
3条回答
  • 2020-12-02 15:21

    You need to write your own IControllerFactory (or perhaps derive from DefaultControllerFactory) and then register it with ControllerBuilder.

    0 讨论(0)
  • 2020-12-02 15:28

    Absolutely! You'll need to override the DefaultControllerFactory to find a custom controller if one doesn't exist. Then you'll need to write an IActionInvoker to handle dynamic action names.

    Your controller factory will look something like:

    public class DynamicControllerFactory : DefaultControllerFactory
    {
        private readonly IServiceLocator _Locator;
    
        public DynamicControllerFactory(IServiceLocator locator)
        {
            _Locator = locator;
        }
    
        protected override Type GetControllerType(string controllerName)
        {
            var controllerType = base.GetControllerType(controllerName);
                // if a controller wasn't found with a matching name, return our dynamic controller
            return controllerType ?? typeof (DynamicController);
        }
    
        protected override IController GetControllerInstance(Type controllerType)
        {
            var controller = base.GetControllerInstance(controllerType) as Controller;
    
            var actionInvoker = _Locator.GetInstance<IActionInvoker>();
            if (actionInvoker != null)
            {
                controller.ActionInvoker = actionInvoker;
            }
    
            return controller;
        }
    }
    

    Then your action invoker would be like:

    public class DynamicActionInvoker : ControllerActionInvoker
    {
        private readonly IServiceLocator _Locator;
    
        public DynamicActionInvoker(IServiceLocator locator)
        {
            _Locator = locator;
        }
    
        protected override ActionDescriptor FindAction(ControllerContext controllerContext,
                                                       ControllerDescriptor controllerDescriptor, string actionName)
        {
                // try to match an existing action name first
            var action = base.FindAction(controllerContext, controllerDescriptor, actionName);
            if (action != null)
            {
                return action;
            }
    
    // @ray247 The remainder of this you'd probably write on your own...
            var actionFinders = _Locator.GetAllInstances<IFindAction>();
            if (actionFinders == null)
            {
                return null;
            }
    
            return actionFinders
                .Select(f => f.FindAction(controllerContext, controllerDescriptor, actionName))
                .Where(d => d != null)
                .FirstOrDefault();
        }
    }
    

    You can see a lot more of this code here. It's an old first draft attempt by myself and a coworker at writing a fully dynamic MVC pipeline. You're free to use it as a reference and copy what you want.

    Edit

    I figured I should include some background about what that code does. We were trying to dynamically build the MVC layer around a domain model. So if your domain contained a Product class, you could navigate to products\alls to see a list of all products. If you wanted to add a product, you'd navigate to product\add. You could go to product\edit\1 to edit a product. We even tried things like allowing you to edit properties on an entity. So product\editprice\1?value=42 would set the price property of product #1 to 42. (My paths might be a little off, I can't recall the exact syntax anymore.) Hope this helps!

    0 讨论(0)
  • 2020-12-02 15:28

    After a little more reflection, there may be a bit simpler way for you to handle the dynamic action names than my other answer. You'll still need to override the default controller factory. I think you could define your route like:

    routes.MapRoute("Dynamic", "{controller}/{command}/{id}", new { action = "ProcessCommand" });
    

    Then on your default/dynamic controller you'd have

    public ActionResult ProcessCommand(string command, int id)
    {
       switch(command)
       {
          // whatever.
       }
    }
    
    0 讨论(0)
提交回复
热议问题