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

发布于 2024-09-11 01:01:41 字数 423 浏览 4 评论 0原文

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

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

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

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

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

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 BlogController.

But my {controller} portion of the url is not decided pre-hand, but it is dynamically determined at run time, how do I create a "dynamic controller" that maps anything to the same controller which then based on the value and determines what to do?

Same thing with {action}, if the {action} portion of my url is also dynamic, is there a way to program this scenario?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

能怎样 2024-09-18 01:01:41

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

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

编辑

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

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!

安穩 2024-09-18 01:01:41

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

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

然后在你的默认/动态控制器上你会有

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

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.
   }
}
浅沫记忆 2024-09-18 01:01:41

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

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

物价感观 2024-09-18 01:01:41

我正在 .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);
}

Iam working with it in .Core but i'll share it's MVC version for all, after that i will share the core version

                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;

// some codes

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

// and

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文