Using the out-of-the-box method locators in ASP.NET MVC (3 or 4DP), is there a way to have the MVC framework differentiate between a string and Guid without needing to parse
First, you must disambigute your methods by giving them two different names:
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);
}
Next, you need a custom route handler to inspect the request, and change the method name accordingly:
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;
}
}
Finally, add a Details
route at the top of your routes, as follows:
routes.Add("Details",
new Route("{controller}/Details/{id}",
new RouteValueDictionary(
new { controller = "Home", action = "Details" }),
new MyRouteHandler()
)
);
);
When a request comes in for details, the Details
route will use your custom route handler to inspect the id
token. The route handler adds to the action name based on the form of the id token, so that the request will be directed to the appropriate action.
If you're still registering routes in this way then the GuidRouteConstraint() class was added in a newer version of MVC and should be used instead of a custom implementation:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Guid",
"{controller}/{action}/{guid}",
new { controller = "Home", action = "Index" },
new { guid = new GuidRouteConstraint() }
);
}
Then you can simply create your action result as:
public class HomeController : Controller {
public ActionResult Index(Guid guid) {
}
}
My opinion is that using action method selector is more usable and less coding.
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;
}
}
This selector inspects request for ID parameter. If it's guid, it returns true. So, to use it:
public class HomeController : Controller
{
[GuidMethodSelector]
public ActionResult Index(Guid id)
{
return View();
}
public ActionResult Index(string id)
{
return View();
}
}