【问题标题】:How to achieve a dynamic controller and action method in ASP.NET MVC?如何在 ASP.NET MVC 中实现动态控制器和动作方法?
【发布时间】:2011-03-19 05:49:53
【问题描述】:

在 Asp.net MVC 中,url 结构类似于

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

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

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

与 {action} 相同,如果我的 url 的 {action} 部分也是动态的,有没有办法对这种情况进行编程?

【问题讨论】:

    标签: asp.net-mvc-2 asp.net-mvc-routing asp.net-mvc-controller


    【解决方案1】:

    您需要编写自己的IControllerFactory(或者可能派生自DefaultControllerFactory),然后将其注册到ControllerBuilder

    【讨论】:

    • 我不知道,因为在 IControllerFactory 的 CreateController 方法中,RequestContext 参数已经包含了错误的路由数据。
    【解决方案2】:

    绝对!如果自定义控制器不存在,您需要覆盖 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;
        }
    }
    

    那么你的动作调用者会是这样的:

    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();
        }
    }
    

    您可以看到更多此代码here。这是我和一位同事编写完全动态 MVC 管道的旧初稿尝试。您可以随意将其用作参考并复制您想要的内容。

    编辑

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

    【讨论】:

    • 你好@Ryan。我试图从github.com/ryanohs/DynamicServices 实现您的 DynamicControllerFactory 和 DynamicActionInvoker。在运行时,我被要求提供 DynamicControllerFactory 1st 的默认构造函数。然后我添加了一个空实现。但是永远不会调用参数化构造函数,并且永远不会设置 _locater 值。因此,在调用 GetControllerInstance 期间,由于 null _locator 未设置 actionInvoker。我哪里做错了。需要我为 _locator 提供默认值,或者它是在运行时从其他地方设置的
    • 任何人都可以详细说明需要动态控制器和动态动作调用程序的场景吗?
    • @Thomas 有很多正当理由。我正在使用它,所以我可以从启动时编写紧凑的服务。动态添加一个方法 tona 通用 crud 控制器,而不必每次都编写一个代码
    【解决方案3】:

    经过更多思考,您可能有比我的其他答案更简单的方法来处理动态动作名称。您仍然需要覆盖默认控制器工厂。我认为您可以像这样定义您的路线:

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

    然后在您的默认/动态控制器上,您将拥有

    public ActionResult ProcessCommand(string command, int id)
    {
       switch(command)
       {
          // whatever.
       }
    }
    

    【讨论】:

    • 动态控制器怎么样?
    • 这个很有限,如果你需要额外的参数怎么办
    【解决方案4】:

    我在 .Core 中使用它,但我会为所有人分享它的 MVC 版本,之后我将分享核心版本

                    case OwnerType.DynamicPage:
                        var dp = mediator.Handle(new Domain.DynamicPages.DynamicPageDtoQuery { ShopId = ShopId, SeoId = seoSearchDto.Id }.AsSingle());
                        if (dp != null)
                        {
                            return GetDynamicPage(dp.Id);
                        }
                        break;
    

    // 一些代码

        private ActionResult GetDynamicPage(int id)
        {
            var routeObj = new
            {
                action = "Detail",
                controller = "DynamicPage",
                id = id
            };
    
            var bController = DependencyResolver.Current.GetService<DynamicPageController>();
            SetControllerContext(bController, routeObj);
            return bController.Detail(id);
        }
    

    // 和

    private void SetControllerContext(ControllerBase controller, object routeObj)
    {
        RouteValueDictionary routeValues = new RouteValueDictionary(routeObj);
    
        var vpd = RouteTable.Routes["Default"].GetVirtualPath(this.ControllerContext.RequestContext, routeValues);
    
    
    
        RouteData routeData = new RouteData();
    
        foreach (KeyValuePair<string, object> kvp in routeValues)
        {
            routeData.Values.Add(kvp.Key, kvp.Value);
        }
    
        foreach (KeyValuePair<string, object> kvp in vpd.DataTokens)
        {
            routeData.DataTokens.Add(kvp.Key, kvp.Value);
        }
    
    
        routeData.Route = vpd.Route;
        if (routeData.RouteHandler == null)
            routeData.RouteHandler = new MvcRouteHandler();
    
    
        controller.ControllerContext = new ControllerContext(this.ControllerContext.HttpContext, routeData, controller);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-16
      • 2016-01-14
      相关资源
      最近更新 更多