4

I have a default route set up in my ASP.NET MVC2 project and would like to add/modify it for some of my other controllers. Lets say I have a Customer controller with Details action that expects and "id" parameter (int). For example:

    //
    // GET: /Customer/Details/5
    public ActionResult Details(int id)
    {
      //...
    }

How can I add a route that will return a 404 if a user enters a "non-number"? I tried adding following "before" default route but it did not work...

    routes.MapRoute(
        "DefaultDigitsId", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Home", action = "Index" },
        new { id = @"\d+" }
        );

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );

Note, I would like, if possible, to keep default route... All of my controllers use "Details" and "Edit" where "id" (int) parameter is required.. I am wondering if there is a way to accomplish this without having to specify multiple routes (i.e. something generic)...And of course, the goal is that if user enters something like "/Customer/Details/apple" it does not throw an error but takes them to Error page...

There is also this post that hints to setting a default value but I am not sure how to do it...

Community
  • 1
  • 1
zam6ak
  • 7,229
  • 11
  • 46
  • 84
  • So "Customer/Details/1" should use DefaultDigitsId route, and "Customer/Details/apple" should give you a 404, right? What URLs would you expect to use your default route? – StriplingWarrior Dec 09 '10 at 23:43
  • I am guessing something like /Customer/Index or /Customer/Create...I see you point though...Are you hinting that I could remove "id" from Default route as it will not be used? – zam6ak Dec 09 '10 at 23:55
  • I wasn't really trying to make a point: just understand the behavior you're going for. – StriplingWarrior Dec 09 '10 at 23:59

3 Answers3

3

I haven't tried this, but you might want to give it a try:

routes.MapRoute(
    "DefaultDetails",
    "{controller}/Details/{id}",
    new { controller = "Home", action = "Details" },
    new { id = @"\d+" }
    );

routes.MapRoute(
    "DefaultEdit",
    "{controller}/Edit/{id}",
    new { controller = "Home", action = "Edit" },
    new { id = @"\d+" }
    );

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

I would expect that "Customer/Details/1" uses the first route (which validates the id as a number, a call to "Customer/Edit/1" uses the second, which does likewise, and a call to "Customer/Buy/Orange" uses the third route, which doesn't try to validate IDs in this way. Did I understand what you're trying to do correctly? Let me know if this approach works.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • Ok. Your approach works a little bit better than having a route with constraint (same as default but with id matched against regex) and having the default route but removing id from it. Thanks – zam6ak Dec 10 '10 at 15:05
2

Add an IgnoreRoute after the one that matches a numeric id that will ignore any requests to the Details action on the Customer controller:

routes.MapRoute(
  "DefaultDigitsId", // Route name
  "{controller}/{action}/{id}", // URL with parameters
  new { controller = "Home", action = "Index" },
  new { id = @"\d+" } // match numeric id
);

routes.IgnoreRoute("Customer/Details/{*pathInfo}");

routes.MapRoute(
  "Default", // Route name
  "{controller}/{action}/{id}", // URL with parameters
  new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113
  • Would I have to add ignores for all other actions and controllers...There is about 5 Controllers each having at least 2 actions requiring "id"...And it will grow... – zam6ak Dec 09 '10 at 23:32
  • If you want to ONLY accept id if it is numeric, simply take "/{id}" off the "Default" route – Jason Goemaat Dec 09 '10 at 23:47
  • lol:) exact same thought I had a second ago while replying to comment on top by @StriplingWarrior – zam6ak Dec 09 '10 at 23:56
0

Any value that cannot be parsed to a string will make the id value be null on call, so you could redirect to the error page if the id is not null.

Another possibility is registering a global filter that checks if there's a parameter named id and if it is null, then redirects to the error page

Diego
  • 5,024
  • 6
  • 38
  • 47
  • How can I check if id param is null when it not declared null-able? Furthermore, I hope I should not have to declare it as "int? id" just so I can check if it is null (and then try to parse it also)... – zam6ak Dec 09 '10 at 23:30
  • You're right, I hadn't noticed that. It's using int? or the other solutions posted above – Diego Dec 10 '10 at 02:13
  • I think declaring the parameter as int? will be the lesser of the evils as that will allow the routing system to do its job. In your controller you'll need to return error when the value is null *or* a numeric value that is not found. This might not be a bad approach considering that most likely you will need to validate the int value anyway to see if it is a valid ID. – Hector Correa Dec 10 '10 at 14:58
  • @Hector I would have to respectfully disagree with you :) Marking a parameter null-able just so you can test its validity, in my opinion, should be "job" of a framework. I do have business service which gets a customer based on id, and if not found throws business exception (CustomerNotFoundException) which I can catch in the controller. But that is "business level validation" not "mvc parameter validation"...Separate concerns... – zam6ak Dec 10 '10 at 15:04
  • @zam6ak Perhaps you could share that with the guys developing the MVC framework, I think your point is a valid one. For example, in the Play! Framework (a Java MVC Framework) if the value cannot be parsed, the router returns an error. – Diego Dec 11 '10 at 05:43