-
Notifications
You must be signed in to change notification settings - Fork 218
Routing Basics
Note: This page assumes you have prior knowledge of how to configure routing for use with MVC. If not, you can read up on routing in Understanding Routing in ASP.NET MVC or on MSDN.
MvcSiteMapProvider typically uses the ASP.NET routing framework as the way to determine when there is a matching sitemap node (it can also use URL or key to find a match). This node matching is what determines which node is the "current" node, so understanding how it works is very useful for troubleshooting problems with the configuration.
By default, MvcSiteMapProvider uses controller, action, and (optionally) area route values and recognizes them natively. However, when using other route values you will need to do some additional setup in order for the node matching to work properly.
For example, imagine you want to use the default route from the MVC template:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
Also, you want to use the optional id parameter in a Product controller:
public ActionResult Details(int id)
{
var product = GetProduct(id);
return View(product);
}
Here is what the XML configuration would typically look like:
<mvcSiteMapNode title="Products" controller="Product" action="Index">
<mvcSiteMapNode title="Details" action="Details" />
</mvcSiteMapNode>
The default behavior automatically inherits the controller (Product in this case) from its parent. Also, the id parameter is ignored by default so you will have to programmatically add the parameter or use a preservedRouteParameters attribute for it to be considered as part of the node match.
Note: PreservedRouteParameters are generally only useful for making the SiteMapPath (breadcrumb trail) HTML helper work alongside navigation that is based on a list or table of database records. However, if you want the items to appear in the Menu or SiteMap HTML helpers or appear in the
/sitemaps.xml
endpoint (the sitemaps XML for search engines), you should use a dynamic node provider to add a node for every record as shown in Programmatically Adding the Route Parameter.
You can use the preservedRouteParameters attribute to preserve the parameter right in the XML configuration:
<mvcSiteMapNode title="Products" controller="Product" action="Index">
<mvcSiteMapNode title="The Product" action="Details" preservedRouteParameters="id" />
</mvcSiteMapNode>
And your action method would look something like this:
public ActionResult Details(int id)
{
var product = GetProduct(id);
return View(product);
}
The preservedRouteParameters will force every "id" that is passed in from the URL to match the node titled "The Product".
The SiteMapPath would look like this:
URL | Sitemap Path |
---|---|
Product/Details/1 |
Home > Products > The Product |
Product/Details/2 |
Home > Products > The Product |
Typically, you will also want to set the title to the same as the product. This can be done using the SiteMapNodeTitleAttribute.
[SiteMapTitle("Title")]
public ActionResult Details(int id)
{
var product = GetProduct(id);
// The SiteMapTitleAttribute will automatically read the product.Title
// property and use it to set the title property of the node.
return View(product);
}
As a result, you would get sitemap paths similar to this:
URL | Sitemap Path |
---|---|
Product/Details/1 |
Home > Products > Product 1 |
Product/Details/2 |
Home > Products > Product 2 |
The SiteMapNodeTitleAttribute also supports targeting the parent node. Consider the following action method:
[SiteMapTitle("Title")]
[SiteMapTitle("Genre.Name", Target = AttributeTarget.ParentNode)]
public ActionResult Details(int id)
{
var album = storeDB.Albums
.Single(a => a.AlbumId == id);
return View(album);
}
By specifying two SiteMapNodeTitleAttribute with a different Target assigned, you would get sitemap paths similar to this:
URL | Sitemap Path |
---|---|
Product/Details/1 |
Home > Genre A > Product 1 |
Product/Details/2 |
Home > Genre B > Product 2 |
As you can see this configuration above isn't always practical. Typically products would be put into a database, in which case you would want to configure your nodes based on database records, not XML. This is where the IDynamicNodeProvider interface comes in handy. Create a small class inheriting DynamicNodeProviderBase:
public class MyDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
// Create a node for each product
foreach (var product in GetProducts())
{
DynamicNode dynamicNode = new DynamicNode("ProductID_" + product.Id, product.Title);
// Preserve our route parameter explicitly
dynamicNode.RouteValues.Add("id", product.id);
yield return dynamicNode;
}
}
}
Then add your new provider to the XML configuration:
<mvcSiteMapNode title="Products" controller="Product" action="Index">
<mvcSiteMapNode title="Details" action="Details" dynamicNodeProvider="MyNamespace.MyDynamicNodeProvider, MyAssembly"/>
</mvcSiteMapNode>
The result looks the same, but we will have nodes added automatically as they are added to the database (after the MvcSiteMapProvider's cache expires). This means that in this example, there will actually be 2 separate nodes instead of one.
URL | Sitemap Path |
---|---|
Product/Details/1 |
Home > Products > Product 1 |
Product/Details/2 |
Home > Products > Product 2 |
For a more in-depth look at how the node matching process works, read How to Make MvcSiteMapProvider Remember a User's Position.
Sometimes you will want to specify a route explicitly to override the default route order. This can be done using the route attribute. Let's add a route to the default routing setup.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
routes.MapRoute(
name: "MyRoute",
url: "Manage/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
Note that this is generally not the proper way to setup routing because the first route will catch anything and the second route will never be hit. We can override that behavior by specifying the route explicitly.
<mvcSiteMapNode title="Products" controller="Product" action="Index" route="MyRoute"/>
This will force the match to use the route named MyRoute and ignore the route named Default.
Want to contribute? See our Contributing to MvcSiteMapProvider guide.
- Upgrading from v3 to v4
- Routing Basics
- Configuring MvcSiteMapProvider
- Defining Sitemap Nodes in XML
- Defining Sitemap Nodes using .NET Attributes
- Defining Sitemap Nodes using IDynamicNodeProvider
- HtmlHelper Extensions
- Controlling URL Behavior
- Using Action Filter Attributes
- Sitemaps XML Protocol Endpoint for Search Engines
- Using Custom Attributes on a Node
- Specifying Node Order
- Advanced Node Visibility
- Multiple Navigation Paths to a Single Page
- Multiple Sitemaps in One Application
- Security Trimming
Other places around the web have some documentation that is helpful for getting started and finding answers that are not found here.
- MvcSiteMapProvider 4.0 - A Test Drive
- MvcSiteMapProvider 4.0 - SEO Features Tutorial
- How to Make MvcSiteMapProvider Remember a User’s Position
- MvcSiteMapProvider 4.0 - Cache Configuration
- MvcSiteMapProvider 4.0 - Extending the Cache
- MvcSiteMapProvider 4.0 - Unit Testing with the SiteMaps Static Methods
- Debugging an MvcSiteMapProvider Configuration
- Converting from C# to Vb MvcSiteMapProvider
- ASP.NET MVC Menu using Site Map Provider & Bootstrap 3 Navbar
- ASP.NET MVC SiteMapPath using Site Map Provider & Bootstrap Breadcrumbs
- NightOwl888's MvcSiteMapProvider Demos - Filter for "MvcSiteMapProvider" to see the most relevant.
- MvcSiteMapProvider Tutorial and Examples
- MvcSiteMapProvider Tutorial 2 - Breadcrumbs
- Getting Started with MvcSiteMapProvider
- Inside the MvcSiteMapProvider - Part 1
- Inside the MvcSiteMapProvider - Part 2: Dynamic node providers
- Inside the MvcSiteMapProvider - Part 3: The ISiteMapVisibilityProvider
- Inside the MvcSiteMapProvider - Part 4: The IAclModule
- Inside the MvcSiteMapProvider - Part 5: The ISiteMapNodeUrlResolver
- Styling MvcSiteMapProvider with CSS
- Using MvcSiteMapProvider with Twitter Bootstrap
- ASP.NET MVC Menu using Site Map Provider & Bootstrap Navbar