【问题标题】:Differentiate Guid and string Parameters in MVC 3区分 MVC 3 中的 Guid 和字符串参数
【发布时间】:2011-09-27 02:48:06
【问题描述】:

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

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

约束执行并通过,因此参数被发送到一个动作,但看起来仍然是一个字符串,因此对于两个方法签名来说是模棱两可的。我预计操作方法的定位方式会导致歧义,因此可以通过插入自定义模块来定位方法来覆盖。

同样的结果可以通过解析字符串参数来实现,但为了简洁起见,避免在操作中出现这种逻辑(更不用说希望以后重用)。

【问题讨论】:

  • 你说得对,动作方法定位器忽略了路由约束。如果删除字符串操作方法,是否会选择 guid 操作方法?
  • MVC 不支持仅基于签名的方法重载 - 对您来说最简单的解决方案可能是简单地拥有两个唯一命名的操作方法,一个用于 GUID(详细信息)的详细信息,另一个用于获取详细信息按名称(可能是搜索还是信息?)。
  • @bzlm - 正确,删除字符串操作将选择 Guid(假设它通过约束或可以解析为 Guid)。

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


【解决方案1】:

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

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 令牌的形式添加到动作名称,以便将请求定向到适当的动作。

【讨论】:

    【解决方案2】:

    我的看法是使用action方法选择器更实用,编码更少。

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

    【讨论】:

    • 您可以使用内置的 GuidRouteConstraint 类而不需要添加自定义代码。我很感激在撰写本文时可能并非如此。
    【解决方案3】:

    如果您仍在以这种方式注册路由,则在较新版本的 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) {
        }
    }
    

    【讨论】:

    • 辛苦了,只要确保您使用正确的 using 语句 - 您可能需要 using System.Web.Mvc.Routing.Constraints; 而不是 System.Web.Http.Routing.Constraints
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    相关资源
    最近更新 更多