使用ASP.NET MVC(3或4DP)中的开箱即用方法定位器,有没有办法让MVC框架区分字符串和Guid,而无需在控制器动作中解析参数?
用法示例适用于网址
http://[domain]/customer/details/F325A917-04F4-4562-B104-AF193C41FA78
执行
public ActionResult Details(Guid guid)
方法和
执行
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 :(得分: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) {
}
}