views:

1905

answers:

5

What's the best way to handle a visitor constructing their own URL and replacing what we expect to be an ID with anything they like?

For example:

http://stackoverflow.com/questions/236349

But the user could just as easily replace the URL with:

http://stackoverflow.com/questions/foo

I've thought of making every Controller Function parameter a String, and using Integer.TryParse() on them - if that passes then I have an ID and can continue, otherwise I can redirect the user to an Unknown / not-found or index View.

Stack Overflow handles it nicely, and I'd like to too - how do you do it, or what would you suggest?

A: 

The problem with that approach is that they still might pass an integer which doesn't map to a page. Just return a 404 if they do that, just as you would with "foo". It's not something to worry about unless you have clear security implications.

Ovid
Sure, I'd have to check the Integer is valid, but is using String as the Function parameter type the way to go? - the TaskList example (http://www.asp.net/learn/mvc/tutorial-01-cs.aspx ~35min 43s) strongly types the ID to Integer and this results in an error when 'foo' is passed in :o/
Andrew
+3  A: 

In ASP.NET MVC, you can define a filter implementing IActionFilter interface. You will be able to decorate your action with this attribute so that it will be executed on, before or after your action.

In your case, you will define it to be executed "before" your action. So that, you will be able to cancel it if there is an error in the passed parameters. The key benefit here that you only write the code which checking the passed paramaters once (i.e you define it in your filter) and use it wherever you want in your controller actions.

Read more about MVC filters here: http://haacked.com/archive/2008/08/14/aspnetmvc-filters.aspx

mnour
+2  A: 

You can specify constraints as regular expressions or define custom constraints. Have a look at this blog post for more information:

http://weblogs.asp.net/stephenwalther/archive/2008/08/06/asp-net-mvc-tip-30-create-custom-route-constraints.aspx

You will still need to deal with the situation where id 43243 doesn't map to anything which could be dealt with as an IActionFilter or in your controller directly.

Duncan
+4  A: 

Here is some useful infromation that might help. If you have a action method

public ActionResult Edit(int? id)
{}

then if someone types in

/Home/Edit/23

the parameter id will be 23. however if someone types in

/Home/Edit/Junk

then id will be null which is pretty cool. I thought it would throw a cast error or something. It means that if id is not a null value then it is a valid integer and can be passed to your services etc. for db interaction.

Hope this provides you with some info that I have found whilst testing.

Schotime
Additionally, there will be a model error present in ModelState which reflects the problem with parameter "id".
Brad Wilson
+7  A: 

Here's an example of a route like yours, with a constraint on the number:

routes.MapRoute(
    "Question",
    "questions/{questionID}",
    new { controller = "StackOverflow", action = "Question" },
    new { questionID = @"\d+" }
);

Here we set the questionID to have at least one number. This will also block out any urls containing anything but an integer, and also prevents the need for a nullable int.

If the user enters the url "questions/foo", they will not hit the Question action, and fall through it, because it fails the parameter constraint. You can handle it further down in a catchall/default route if you want:

routes.MapRoute(
    "Catchall",
    "{*catchall}",
    new { controller = "Home", action = "Lost" }
);

This will send the user to the Lost action in the Home controller, where you may want to punish them for their sillyness. :-)

Please note that this should reside as the LAST route. Placing it further up the chain will mean that this will handle all others below it, given the lazy nature of routes in ASP.NET MVC.

Dan Atkinson
this looks like a pretty good way to handle crazy users =)
Omar
Instead of that last line: 'new { questionID = @"\d{1,}" }', Scott Hanselman uses a slightly shorter syntax of: 'new { questionID = @"\d+" }' to mean the same thing. Source: http://www.asp.net/learn/mvc-videos/video-7093.aspx
Andrew
Yes, the two are pretty much the same thing. I tend to put mine in curly braces like that because I dynamically create my routes from the database, and it saves me a bit of logic when I want to explicitly specify the maximum number of digits.
Dan Atkinson