区分MVC 3中的Guid和字符串参数

时间:2011-09-27 02:48:06

标签: asp.net-mvc asp.net-mvc-routing guid

使用ASP.NET MVC(3或4DP)中的开箱即用方法定位器,有没有办法让MVC框架区分字符串和Guid,而无需在控制器动作中解析参数?

用法示例适用于网址

  

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

约束执行并传递,因此参数被发送到一个动作,但看起来仍然是一个字符串,因此对两个方法签名不明确。我希望动作方法的位置会导致模糊,因此可以通过插入自定义模块来定位方法来覆盖。

通过解析出字符串参数可以获得相同的结果,但为了简洁起见,在行动中避免使用该逻辑(更不用说有希望在某天之后再使用),这样做非常好。

3 个答案:

答案 0 :(得分:11)

首先,您必须通过给出两个不同的名称来消除歧义:

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标记的形式添加到操作名称,以便将请求定向到相应的操作。

答案 1 :(得分: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();
    }
}

答案 2 :(得分:7)

如果您仍然以这种方式注册路由,那么GuidRouteConstraint()类已添加到较新版本的MVC中,应该用于代替自定义实现:

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