开发者

Differentiate Guid and string Parameters in MVC 3

开发者 https://www.devze.com 2023-04-08 09:42 出处:网络
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 the parameter in the contr

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 the parameter in the controller action?

Usage examples would be for the URL

http://[domain]/customer/details/F325A917-04F4-4562-B104-AF193C41FA78开发者_运维百科

to execute the

public ActionResult Details(Guid guid)

method, and

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

to execute the

public ActionResult Details(string id)

method.

With no changes, obviously the methods are ambiguous, as follows:

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

resulting in the error:

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 

I attempted to use a custom constraint (based on How can I create a route constraint of type System.Guid?) to try and push it through via routing:

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

And switched the action signatures to:

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

The constraint executes and passes, thus the argument is sent to an action, but seemingly still as a string, and thus ambiguous to the two method signatures. I expect that there's something in how the action methods are located that causes the ambiguity, and thus could be overridden by plugging in a custom module to locate methods.

The same result could be achieved by parsing out the string parameter, but would be really nice for brevity to avoid that logic in the action (not to mention to hopefully reuse someday later).


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.


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


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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号