MVC ActionResult Methods, how are parameters matched?

I will try to word this as best as I can. Lets assume I have one method in controller X:

Index(string someString)

Is this valid? : X/Index

or do I need: X/Index/someString

(All this using the default routing configuration).

Now, is it possible to have a second method, so that:

Index(stringSomestring)
{
..
}

Index()
{
}

And if so, how does MVC route choose the right method?

Finally...

What is the difference between : declaring a parameter default value in a routeConfiguration, or in the method Parameters itself?

e.g. :

rotes.Add("", "etcetc", new {someString = string.Empty);

vs

Index(string someString = string.Empty)

Thanks and sorry if it's too silly or verbose.

---- EDIT ----- Thanks for your response anaximander, you've helped me clear my head some (though still struggling). Consider this :

routes.MapRoute("",
                "Student/Sort-By{sortType}",
                new { controller="Student", action="Index", sortType= ""}
                );

I have a method:

Index(string sortType)
{
...
}

If I provide a url : Student/Sort-ByDate it works fine. If I provide a url : Student/Sort-By it finds nothing, though my method has an option for empty sort...

I feel like I am overcomplicating/doing something stupid, but can't pinpoint what :(

Upvotes: 0

Views: 885

Answers (2)

anaximander
anaximander

Reputation: 7140

It depends on your routing setup. The default route looks like this:

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

Note the UrlParameter.Optional part. That means you can go to somehost.com/X/Index and it will look for a method Index(), or you can add another slash and a parameter, and it'll look for Index(foo id) where foo is any type you fancy.

Routes are applied in the order you add them. That means that if you start at the top of your RouteConfig and read down, the first one that would match the URL is the one it'll use. This is why you should put more specific routes above more general ones - otherwise, the general routes will catch all the URLs and stop the more specific routes from being used.

The system looks for:

  • A controller that matches the {controller} portion of the URL (or the default controller if the URL doesn't specify one)
  • All action methods on that controller that match the {action} part of the URL (or the default action if the URL doesn't specify one)
  • Any of those methods that have parameter names matching the set of parameters extracted from the URL, combined with those specified in the route.
    • This means that for a method Index(int id), if the URL contains an {id} portion, it'll use that method. If that portion is absent, but the route specifies a default value, then it'll use that method with the default value. If there is no value, no default, and the parameter is marked as UrlParameter.Optional, it will look for another method that doesn't require id. (If the parameter is required, then this route wouldn't have matched, because the URL hasn't provided one.)
    • If the system can't decide which method you're trying to call, you'll get an exception telling you that the route was ambiguous.

So, in your case, if you want to be able to use somehost.com/X/Index to get to Index(string someString) then you'll need a route like this:

routes.MapRoute(
    name: "Index",
    url: "{controller}/{action}/{someString}",
    defaults: new { controller = "X", action = "Index", someString = String.Empty }
);

which will call Index(string someString) with the empty string if the URL doesn't give one. If you want URLs without a someString parameter to go to Index, then instead you can set somestring = UrlParameter.Optional and if the URL doesn't provide one, it'll look for a method that doesn't need it. If you leave someString out of the defaults entirely, then any URL that doesn't provide one will be invalid for this route, and will have to match some other route instead. If you've still got the pre-provided default route at the bottom, then it will catch somehost.com/X/Index because it fits that template, so it'll assume you've omitted the optional id parameter and look for a method like Index().

Edit:

When combining URL fluff and parameters like this, it gets a little trickier. Without being able to run it myself to debug, I'd guess that as far as the system is concerned, when matching Sort-By{sortType}, the string Sort-By is not "Sort-By" + "", it's "Sort-By" + null. That is, the URL doesn't give the value of {sortType} as a blank string, because the blank string is still a string, whereas this URL contains no string at all in that position. If the URL doesn't provide a value for {sortType} and it's not marked as optional, then the URL doesn't match this route. This might work better as:

routes.MapRoute("",
    "Student/Sort/{sortType}",
    new { controller="Student", action="Index", sortType= ""}
);

Alternatively, you could consider dropping the sort from the URL and using query parameters, so the route becomes

routes.MapRoute("",
    "Student/",
    new { controller="Student", action="Index", sortBy= ""}
);

and to sort you go to host.com/Student/?sortBy=Date.

Upvotes: 2

AMS
AMS

Reputation: 439

You cannot have several Index() for your routing

If you want to use either X/Index or X/Index/someText

You will have to declare ActionResult Index(string someString)

The routing process will know that Index can accept a String and then it will redirect to the right action. You don't need to render the string nullable or to specify an empty string. the string will simply be null.

To "guess" the right action i think the mvc framework uses reflection and associate Types and Names of the parameters.

The routing process will always work but if you want to specify a route that will be different from the default way then you will have to :

rotes.Add("", "etcetc", new {someString = string.Empty);

Upvotes: 0

Related Questions