如何在ASP.NET MVC中实现动态控制器和动作方法?

Ray*_*Ray 26 asp.net-mvc-routing asp.net-mvc-controller asp.net-mvc-2

在Asp.net MVC中,网址结构如下

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

对于每个"控制器",比如http://example.com/blog,都有一个BlogController.

但是我的{controller}部分的url不是预先决定的,但它是在运行时动态确定的,我如何创建一个"动态控制器",将任何东西映射到同一个控制器,然后根据该值确定什么去做?

与{action}相同,如果我的网址的{action}部分也是动态的,有没有办法对此方案进行编程?

Rya*_*yan 25

绝对!DefaultControllerFactory如果不存在自定义控制器,则需要覆盖它以查找自定义控制器.然后你需要编写一个IActionInvoker来处理动态动作名称.

您的控制器工厂将看起来像:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你的动作调用者就像:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以在此处看到更多此代码.这是我自己和同事在编写一个完全动态的MVC管道时的初步尝试.您可以将其用作参考并复制您想要的内容.

编辑

我想我应该包含一些关于该代码的作用的背景知识.我们试图围绕域模型动态构建MVC层.因此,如果您的域包含Product类,您可以导航products\alls到查看所有产品的列表.如果您想添加产品,请导航至product\add.你可以去product\edit\1编辑一个产品.我们甚至尝试过允许您编辑实体属性的内容.所以product\editprice\1?value=42将产品#1的价格属性设置为42.(我的路径可能有点偏,我不记得确切的语法了.)希望这有帮助!


Rya*_*yan 8

经过一点反思之后,处理动态动作名称可能比我的其他答案更简单一些.您仍然需要覆盖默认控制器工厂.我想你可以定义你的路线,如:

routes.MapRoute("Dynamic", "{controller}/{command}/{id}", new { action = "ProcessCommand" });
Run Code Online (Sandbox Code Playgroud)

然后在您的默认/动态控制器上

public ActionResult ProcessCommand(string command, int id)
{
   switch(command)
   {
      // whatever.
   }
}
Run Code Online (Sandbox Code Playgroud)