2011-09-27 36 views
7

在使用ASP.NET MVC(3或4DP)的外的现成方法定位器,是有办法有一个字符串和GUID之间的MVC架构区分,而无需解析控制器操作中的参数?微分GUID和string参数在MVC 3

用法的例子是用于URL

http://[domain]/customer/details/F325A917-04F4-4562-B104-AF193C41FA78

执行

public ActionResult Details(Guid guid) 

方法,和

http://[domain]/customer/details/bill-gates

执行

public ActionResult Details(string id) 

方法。

由于没有改变,显然这些方法是不明确的,具体如下:

public ActionResult Details(Guid id) 
{ 
    var model = Context.GetData(id); 
    return View(model); 
} 

public ActionResult Details(string id) 
{ 
    var model = Context.GetData(id); 
    return View(model); 
} 

导致错误:

The current request for action 'Details' on controller type 'DataController' is ambiguous between the following action methods: 
System.Web.Mvc.ActionResult Details(System.Guid) on type Example.Web.Controllers.DataController 
System.Web.Mvc.ActionResult Details(System.String) on type Example.Web.Controllers.DataController 

我试图使用自定义的约束(基于How can I create a route constraint of type System.Guid?)尝试并通过路由推送它:

routes.MapRoute(
    "Guid", 
    "{controller}/{action}/{guid}", 
    new { controller = "Home", action = "Index" }, 
    new { guid = new GuidConstraint() } 
); 

routes.MapRoute(
    "Default", // Route name 
    "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
); 

并切换动作签名:

public ActionResult Details(Guid guid) 
{ 
    var model = Context.GetData(guid); 
    return View(model); 
} 

public ActionResult Details(string id) 
{ 
    var model = Context.GetData(id); 
    return View(model); 
} 

约束执行和通过,从而将参数发送到一个动作,但貌似还作为一个字符串,因此不明确的两个方法签名。我预计操作方法的定位会导致模糊,因此可以通过插入定制模块来定位方法来覆盖。

同样的结果可以通过解析出字符串参数来实现,但将是非常好的为简便起见,以避免在行动(更不用说希望有一天以后再使用),其逻辑。

+0

你是对的,动作方法定位器是浑然不觉的路由约束。如果删除字符串操作方法,那么guid操作方法是否被选中? – bzlm

+1

MVC不支持仅基于签名的方法重载 - 最简单的解决方案可能是简单地使用两个唯一命名的操作方法,一个用于GUID(Details)的详细信息,另一个用于获取详细信息(搜索或信息) ?)。 – Tommy

+0

@bzlm - 更正,删除字符串动作将选择Guid(假设它传递约束或可以被解析成Guid)。 – falquan

回答

11

首先,你必须给他们两个不同的名字disambigute你的方法:

public ActionResult DetailsGuid(Guid guid) 
{ 
    var model = Context.GetData(guid); 
    return View(model); 
} 

public ActionResult DetailsString(string id) 
{ 
    var model = Context.GetData(id); 
    return View(model); 
} 

接下来,你需要一个自定义路由处理程序进行相应检查的要求,并更改方法名称:

using System.Web.Mvc; 
using System.Web.Routing; 

public class MyRouteHandler : IRouteHandler 
{ 
    public IHttpHandler GetHttpHandler(RequestContext requestContext) 
    { 
     var routeData = requestContext.RouteData; 
     var stringValue = routeData.Values["id"].ToString(); 
     Guid guidValue; 
     var action = routeData.Values["action"]; 
     if (Guid.TryParse(stringValue, out guidValue) && (guidValue != Guid.Empty); 
      routeData.Values["action"] = action + "Guid"; 

     else 
      routeData.Values["action"] = action + "String"; 

     var handler = new MvcHandler(requestContext); 
     return handler; 
    } 
} 

最后,在你的路由的顶部添加Details路线如下:

routes.Add("Details", 
    new Route("{controller}/Details/{id}", 
     new RouteValueDictionary( 
      new { controller = "Home", action = "Details" }), 
      new MyRouteHandler() 
     ) 
    ); 
); 

当一个请求进入细节,Details路线将使用自定义路由处理程序来检查id令牌。路由处理程序根据id标记的形式添加到操作名称中,以便将请求指向适当的操作。

8

我的意见是,使用的操作方法选择是更可用少的编码。

public class GuidMethodSelectorAttribute : ActionMethodSelectorAttribute 
{ 
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo) 
    { 
     var idStr = controllerContext.RouteData.Values["id"]; 
     if (idStr == null) 
      return false; 
     Guid a; 
     var result = Guid.TryParse(idStr.ToString(), out a); 
     return result; 
    } 
} 

该选择器检查对ID参数的请求。如果是guid,则返回true。因此,使用它:

public class HomeController : Controller 
{ 
    [GuidMethodSelector] 
    public ActionResult Index(Guid id) 
    { 
     return View(); 
    } 
    public ActionResult Index(string id) 
    { 
     return View(); 
    } 
} 
+0

您可以使用内置的GuidRouteConstraint类而不需要添加自定义代码。我明白,撰写本文时可能并非如此。 –

5

如果你还在用这种方式再MVC的新版本中加入GuidRouteConstraint()类注册路线和应改为使用自定义实现的:

public override void RegisterArea(AreaRegistrationContext context) 
{ 
    context.MapRoute(
     "Guid", 
     "{controller}/{action}/{guid}", 
     new { controller = "Home", action = "Index" }, 
     new { guid = new GuidRouteConstraint() } 
    ); 
} 

然后,你可以简单地创建你的行动结果:

public class HomeController : Controller { 
    public ActionResult Index(Guid guid) { 
    } 
} 
+2

工作过,只要确保你使用正确的'using'语句 - 你可能想要'使用System.Web.Mvc.Routing.Constraints;'而不是System.Web.Http.Routing.Constraints –