I am trying to implement alternative routing in my MVC 5 web app.
I have the controller code:
namespace MyMvcProject.Web.Controllers
{
[RoutePrefix("account")]
public class AccountController : Controller {
[Route("{param}")]
[Authorize]
public ActionResult Index(string param = null) {
...
which works great when hitting the url: http://example.com/account/testparam
.
However, I would like to be able to have the value param
as an optional parameter.
I have tried changing [Route("{param}")]
to [Route("{param}*")]
but then the Index()
method is never entered. I have also tried changing it to [Route("{param:string=Test}")]
but I get a routing runtime error with the term string
.
My RoutConfig.cs
contains:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{param}",
defaults: new { controller = "Home", action = "Index", param = UrlParameter.Optional }
);
}
Does anyone have any idea how I could force Index()
to have an optional parameter using this alternative routing syntax? It's quite useful in other parts of my app, so I'd like to keep the RoutePrefix
and Route
decorators.
UPDATE
I'm still trying to figure this out, so I changed my route decorator to [Route("{param:int=0}")]
and my constructor to public ActionResult Index(int id)
and it works as expected (i.e., http://example.com/account
behaves as if http://example.com/account/0
was entered. This is exactly what I want, only using string
datatypes.
When I change the decorator to: [Route("{id:string=\"\"}")]
and the constructor to public ActionResult Index(string id)
and I see the runtime error:
The inline constraint resolver of type 'DefaultInlineConstraintResolver' was unable to resolve the following inline constraint: 'string'.
Found the answer here. I need to make param
nullable using ?
.
[Route("{param?}")]
[Authorize]
public ActionResult Index(string param = null) {
...
}
Hopefully this will help someone in the future. Not many references that I could find on the topic.
@Brett's answer is great: adding ?
to the parameter in the Route attribute and then providing a default value in the Action signature allows that parameter to be optional.
And here's an excellent article from Mike Wasson on Attribute Routing in Web API 2, which includes a piece on optional parameters.
Mike also talks about applying multiple constraints, and says:
You can apply multiple constraints to a parameter, separated by a colon.
[Route("users/{id:int:min(1)}")] public User GetUserById(int id) { ... }
which also works great...until you want multiple constraints and an optional parameter.
As we know, applying ?
allows the parameter to be optional. So taking the above example, one might assume that
[Route("users/{id:int?:min(1)}")]
public User GetUserById(int id = 1) { ... }
constrains the id
parameter to be integers greater than 0, or empty (in which case the default of 1 is used). In reality, we get the error message:
The inline constraint resolver of type 'DefaultInlineConstraintResolver' was unable to resolve the following inline constraint: 'int?'.
It turns out that the order of the constraints matters! Simply putting the optional constraint ?
after all other constraints gives the intended behavior
[Route("users/{id:min(1):int?}")]
public User GetUserById(int id = 1) { ... }
This works for more than 2 constraints as well, e.g.
[Route("users/{id:min(1):max(10):int?}")]
public User GetUserById(int id = 1) { ... }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With