-
Notifications
You must be signed in to change notification settings - Fork 218
Controlling URL Behavior
MvcSiteMapProvider has several options for configuring URLs.
MvcSiteMapProvider has 2 ways of creating URLs:
- Using .NET Routing
- Setting URLs Explicitly
Both of these methods both generate the URLs and provide the framework to match them against the current request, as described in Routing Basics.
To use .NET routing, set the "controller", "action", and optionally the "area" and/or "route" attributes/properties of the node, along with any custom RouteValue information. This will cause the default SiteMapNodeUrlResolver class to call into the MVC framework's UrlHelper class to resolve the URL.
Below is some pseudo-code that mimics what the SiteMapNodeUrlResolver does. You can use this to troubleshoot your URL generation code, if needed.
// Using controller and action
var urlHelper = new UrlHelper(new System.Web.Routing.RequestContext(this.HttpContext, this.RouteData));
var url = urlHelper.Action("View", "Videos", new System.Web.Routing.RouteValueDictionary { { "id", 123 } });
// Using controller, action, and route
var urlHelper = new UrlHelper(new System.Web.Routing.RequestContext(this.HttpContext, this.RouteData));
var url = urlHelper.RouteUrl("RouteName", new System.Web.Routing.RouteValueDictionary { { "controller", "Videos" }, { "action", "View" }, { "id", 123 } });
To set a URL explicitly, you can use the "url" attribute/property of the node. The URL property supports absolute URLs, root relative URLs (beginning with /), and virtual application relative URLs (beginning with ~/).
// Absolute URL
dynamicNode.Url = "http://www.somewhere.com:123/some-application/some-page?a=b";
// Root Relative URL
dynamicNode.Url = "/some-application/some-page?a=b";
// Virtual Application Relative URL
dynamicNode.Url = "~/some-page?a=b";
Clearly, hard-coding the host name is not the most maintainable option. You can also force a URL to be absolute by setting the "protocol" attribute/property of the node. This will work whether using routing or explicit URLs.
<!-- Generates "https://www.sitenamefromrequest.com/Account/Index" -->
<mvcSiteMapNode title="Account Home" controller="Account" action="Index" protocol="https"/>
You can also use the protocol of the current request by specifying "*". This can come in handy if you want to ensure that once the user enters an SSL session, they must stay in the SSL session for the remainder of their visit.
<mvcSiteMapNode title="Home" controller="Home" action="Index" protocol="*">
<mvcSiteMapNode title="Account Home" controller="Account" action="Index" protocol="https"/>
</mvcSiteMapNode>
In the above example, if the user first visits the home page, then clicks the "Account Home" link, the Account/Index page will be requested with https. The home page link will also be created beginning with https, so if the user navigates there next, they will not leave the SSL session.
You can also use the "hostName" attribute/property to make links to other web sites within a multi-tenant application.
<mvcSiteMapNode title="Home" controller="Home" action="About" hostName="www.website1.com">
<mvcSiteMapNode title="Home" controller="Home" action="About" hostName="www.website2.com">
In the above example, both of these links go to the same controller action, but will access it through different host names. If viewing both of these links in the menu, it will select the correct link as the current node even if switching between websites as long as they are both hosted within the current application.
http://www.website1.com/Home/About
http://www.website2.com/Home/About
If using ports for HTTP and HTTPS other than the default ports of 80 and 443, extra configuration may be required in order to get your URLs to resolve to the correct port. This only applies if you are using absolute URLs and you are not using a single protocol throughout your website, but are switching between protocols, as is commonly done between HTTP and HTTPS.
Note: If you use relative URLs, you can use non-standard ports without doing any extra configuration.
By default, MvcSiteMapProvider will attempt to read the ports from IIS or IIS Express if your site is running on IIS or IIS Express. So, for example if IIS is configured with the following bindings, the port 8080 will be used if the domain name is www.somewhere.com
and the protocol is HTTP and the port 4433 will be used if the domain name is www.somewhere.com
and the protocol is HTTPS.
Type | Host Name | Port | IP Address |
---|---|---|---|
http | www.somewhere.com |
8080 | * |
https | www.somewhere.com |
4433 | * |
See the documentation at Technet to configure host header names in IIS 7 or see this for IIS Express.
If configured in the table above, the following nodes will resolve their URLs as described (assuming the current host name is www.somewhere.com
).
<!-- Generates "http://www.somewhere.com:8080/Account/Index" -->
<mvcSiteMapNode title="Account Home" controller="Account" action="Index" protocol="http"/>
<!-- Generates "https://www.somewhere.com:4433/Account/Index" -->
<mvcSiteMapNode title="Account Home" controller="Account" action="Index" protocol="https"/>
Note that it doesn't really matter if the site is hosted locally, MvcSiteMapProvider will use the bindings in IIS or IIS Express and doesn't make any assumptions about whether or not you are actually hosting them. For example, if www.somewhere.com
is hosted somewhere else and you configure your host headers as shown in the above table, the URLs will be created to the external site by adding the host name.
<!-- local site is www.mysite.com -->
<!-- Generates "http://www.mysite.com/" -->
<mvcSiteMapNode title="Home" controller="Home" action="Index" protocol="http"/>
<!-- Generates "http://www.somewhere.com:8080/Account/Index" -->
<mvcSiteMapNode title="Account Home" controller="Account" action="Index" protocol="http" hostName="www.somewhere.com"/>
<!-- Generates "https://www.somewhere.com:4433/Account/Index" -->
<mvcSiteMapNode title="Account Home" controller="Account" action="Index" protocol="https" hostName="www.somewhere.com"/>
Note: This technique can only be done using an external DI container.
You can also provide bindings directly in your DI configuration through the IBindingProvider interface. You can implement this interface yourself if you so choose, but for convenience there is a CustomBindingProvider included in the box. This is how you would use it in your DI configuration (StructureMap example shown).
// Exclude the IBindingProvider type from auto-registration.
var excludeTypes = new Type[] {
typeof(IBindingProvider)
};
// Configure the bindings. Note that an exact match will be considered before "*".
var bindings = new List<IBinding>()
{
new Binding(hostName: "*", protocol: "http", port: 81),
new Binding(hostName: "*", protocol: "https", port: 444),
new Binding(hostName: "www.somewhere.com", protocol: "http", port: 8080),
new Binding(hostName: "www.somewhere.com", protocol: "https", port: 4433),
new Binding(hostName: "www.somewhere.com", protocol: "ftp", port: 1123)
};
// Inject the CustomBindingProvider, and supply the bindings to its constructor.
this.For<IBindingProvider>().Use<CustomBindingProvider>()
.Ctor<IEnumerable<IBinding>>().Is(bindings);
The above configuration will use 8080 for HTTP and 4433 for HTTPS on domain www.somewhere.com
. All other domains will default to 81 for HTTP and 444 for HTTPS.
If you are not satisfied with the default URL scheme, you should configure your routes in MVC to change your URLs rather than trying to extend MvcSiteMapProvider. Doing it this way will ensure that your URLs are consistent in both MVC and MvcSiteMapProvider. MVC allows you to create virtually any URL scheme and for advanced scenarios, you can inherit RouteBase to create your own route implementation.
The ImageUrl property works differently than Url or CanonicalUrl because it builds a content URL instead of a link. What this means is that it is set up to resolve absolute URLs differently. The most convenient way to use it is to set the imageUrlHostName attribute/property to the domain name of your CDN server.
<mvcSiteMapNode title="Home" controller="Home" action="Index" imageUrl="~/Images/my-image.png" imageUrlHostName="cdn.mywebsite.com"/>
When configured like this, it will automatically match the protocol of the current request. So if you access the home page using HTTPS, the imageUrl will resolve to https://cdn.mywebsite.com/Images/my-image.png
.
Do note that none of the included HTML templates use the ImageUrl, but it is available should you build a custom HTML template to use it. However, modern techniques more often use a CSS class name to identify an image in which case you should use a custom attribute instead.
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