ASP.NET MVC enum argument in controller mapping

2019-04-30 13:28发布

问题:

ASP.NET MVC provides simple templates for controller methods such as Details, and can have something like:

public ActionResult Details(int id)
{
    // do something
}

This can be accessed by: http://localhost:port/Controller/Details/id

What I'm trying to do is instead provide a different type like:

public enum MyEnum
{
    All,
    Pending,
    Complete
}

And then I setup my controller method like:

public ActionResult MyMethod(MyEnum myEnum = MyEnum.Pending)
{
    // do something
}

This works fine for: http://localhost:port/Controller/MyMethod/ because it uses the default argument.

To specify a different argument I have to do http://localhost:port/Controller/MyMethod?myEnum=All and that works.

I'm wondering, is it possible for me to be able to do http://localhost:port/Controller/MyMethod/All instead of using ?myEnum=All?

Upon trying to do it that way I get a 404 exception which is understandable, but why doesn't this happen for id in Details?

Can I change the MapRoute which is currently: url: "{controller}/{action}/{id}" to allow me to achieve it with my own type?

What I've tried so far:

I only want this route enforcement for one of my schemes such as http://localhost:port/Controller/MyMethod/{ViewType}, I tried this but it doesn't seem to do anything:

routes.MapRoute(
    "MyRoute",
    "MyController/Index/{MyEnum}",
    new { controller = "MyController", action = "Pending" }
);

回答1:

/Controller/MyMethod/All will actually work. The problem is with the default route, which will consider All to be the id route parameter, which doesn't line up with what your action is using as a parameter. It would actually work fine if your action signature was:

public ActionResult MyMethod(MyEnum id = MyEnum.Pending)

Since it will then bind All to the right thing.

You could add another route for this use-case, but you'll need to be careful that you don't just create another "default" route, which will take over. In other words, you'll have to fix part of the URL:

routes.MapRoute(
    "MyCustomRoute",
    "Controller/{action}/{myEnum}",
    new { controller = "Controller", action = "MyMethod", myEnum = MyEnum.Pending }
);

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }
 );

Then, by the mere presence of the /Controller/ prefix to the route, it will use your custom route instead, and fill in All for the myEnum param, rather than hitting the default route and try to fill in id.

However, be advised that when using enums as route params, they must be exact matches. So, while /Controller/MyMethod/All will work, /Controller/MyMethod/all will not. To get around this, you'll have to create a custom model binder. I did a quick search and found the following article which may help you in that regard.



回答2:

You can indeed. Do not change the default route "{controller}/{action}/{id}", but rather add one before the default. This new one needs to be fairly specific:

routes.MapRoute(
    "EnumRoute",
    "Controller/MyMethod/{myEnum}",
    new { controller = "Controller", action = "MyMethod", myEnum = UrlParameter.Optional }
);

What that basically says is "when you see request to literally Controller/MyMethod/whatever, use this controller and that method and pass whatever as parameter of the request". Note that actual controller does not necessary have to be what route says in the url, although it is a good idea to stick to that.