在Asp.net MVC中,url结构就像
http://example.com/ {控制器} / {行动} / {ID}
对于每个“控制器”,比如http://example.com/blog,都有一个BlogController。
但是我的{controller}部分的url不是预先决定的,但它是在运行时动态确定的,我如何创建一个“动态控制器”,将任何东西映射到同一个控制器,然后根据值并决定该怎么做?
与{action}相同,如果我的网址的{action}部分也是动态的,有没有办法对此方案进行编程?
答案 0 :(得分: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;
}
}
然后你的动作调用者就像:
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的价格属性设置为42.(我的路径可能有点偏差,我不记得确切的语法了。)希望这有帮助!
答案 1 :(得分:7)
经过多一点反思后,处理动态动作名称可能比我的其他答案更简单一些。您仍然需要覆盖默认控制器工厂。我想你可以定义你的路线:
routes.MapRoute("Dynamic", "{controller}/{command}/{id}", new { action = "ProcessCommand" });
然后在你的默认/动态控制器上你有
public ActionResult ProcessCommand(string command, int id)
{
switch(command)
{
// whatever.
}
}
答案 2 :(得分:1)
您需要编写自己的IControllerFactory
(或者可能派生自DefaultControllerFactory
),然后将其注册到ControllerBuilder
。
答案 3 :(得分:0)
我正在 .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);
}