When an incoming URL is received by an ASP.NET MVC Web
Application, the MVC framework evaluates the routing rules in the
RouteTable.Routes collection to determine the appropriate Controller to handle
the request.
The MVC framework chooses the Controller to use by
evaluating the RouteTable rules in the order that they have been
registered. The incoming URL is tested against each Route rule to see if
it matches - and if a Route rule matches then that rule (and its associated
RouteHandler) is the one that is used to process the request (and all
subsequent rules are ignored). This means that you want to typically
structure your routing Rules in a "most specific to least specific"
order.
Routing Scenario: Custom Search URL
Let's walk through using some custom routing rules in a real
world scenario. We'll do this by implementing search functionality for
our e-commerce site.
We'll start by adding a new SearchController class to our
project:
Figure 7
We'll then define two Action methods within our
SearchController class. The Index() action method will be used to present
a search page that has a TextBox for a user to enter and submit a search
term. The Results() action will be used to handle the form submission
from it, perform the search against our database, and then display the results
back to the user:
Figure 8
Using the default /[controller]/[action]/[id]
URL route mapping rule, we could "out of the box" use URLs like below
to invoke our SearchController actions:
Scenario
|
URL
|
Action Method
|
Search Form:
|
/Search/
|
Index
|
Search Results:
|
/Search/Results?query=Beverages
|
Results
|
|
/Search/Results?query=ASP.NET
|
Results
|
Note that the reason the root /Search URL by default maps to
the Index() action method is because the /[controller]/[action]/[id] route
definition added by default when Visual Studio creates a new project sets
"Index" as the default action on Controllers (via the
"Defaults" property):
Figure 9
While URLs like
/Search/Results?query=Beverages are perfectly functional, we might decide we
want slightly "prettier" URLs for our search results.
Specifically we might want to get rid of the "Results" action name
from the URL, and pass in the search query as part of the URL instead of using
a QueryString argument. For example:
Scenario
|
URL
|
Action Method
|
Search Form:
|
/Search/
|
Index
|
Search Results:
|
/Search/Beverages
|
Results
|
|
/Search/ASP.NET
|
Results
|
We could enable these "prettier"
search result URLs by adding two custom URL Route mapping rules before the
default /[controller]/[action]/[id] rule like below:
Figure 10
With the first two rules we are now explicitly
specifying the Controller and Action parameters for /Search/ URLs. We are
indicating that "/Search" should always be handled by the
"Index" action on the SearchController. Any URL with a sub-URL
hierarchy (/Search/Foo, /Search/Bar, etc) is now always handled by the
"Results" action on the SearchController.
The second routing rule above indicates that
anything beyond the /Search/ prefix should be treated as a parameter called
"[query]" that will then be passed as a method parameter to our
Results action method on SearchController:
Figure 11
Most likely we will also want to enable
paginated search results (where we only show 10 search query results at a time).
We could do this via a querystring argument (for example:
/Search/Beverages?page=2) or we could optionally embed the page index number as
part of the URL as well (for example: /Search/Beverages/2). To support this later option all we'd need to-do is add an extra optional parameter to our 2nd
routing rule:
Figure 12
Notice above how the new URL rule match is now
"Search/[query]/[page]". We've also configured the default page
index to be 1 in cases where it is not included in the URL (this is done via
the anonymous type passed as the "Defaults" property
value).
We can then update our SearchController.Results action
method to take this page parameter as a method argument:
Figure 13
And with that we now have "pretty URL" searching
for our site (all that remains is to implement the search algorithm - which I
will leave as an exercise to the reader <g>).
Validation Pre-Conditions for Routing Rules
As I mentioned earlier in this post, the Route class has a
"Validation" property that allows you to add validation pre-condition
rules that must be true (in addition to the URL filter) for a route rule to
match. The ASP.NET MVC Framework allows you to use regular expressions to
validate each parameter argument in the URL, as well as allows you to evaluate
HTTP headers (to route URLs differently based on HTTP verbs).
Below is a custom validation rule we could enable for URLs
like "/Products/Detail/43". It specifies that the ID argument
must be a number (no strings allowed), and that it must be between 1 and 8
characters long:
Figure 14
If we pass in a URL like /Products/Detail/12 to our
application, the above routing rule will be valid. If we pass in
/Products/Detail/abc or /Products/Detail/23232323232 it will not match.