From 93fbf3308c6e76cb217c84f0bc982a86b2b2ea8d Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Fri, 28 Feb 2014 14:19:44 +0700 Subject: [PATCH] Added support for eliminating the ambient request values from being considered when resolving URLs (#213). --- .../Web/Mvc/IMvcContextFactory.cs | 4 +- .../Web/Mvc/MvcContextFactory.cs | 22 +- .../Web/Mvc/UrlHelperAdapter.cs | 4 +- .../Web/UrlResolver/SiteMapNodeUrlResolver.cs | 53 ++- .../Xml/MvcSiteMapSchema.xsd | 319 ++++++++++++++++++ 5 files changed, 385 insertions(+), 17 deletions(-) create mode 100644 src/MvcSiteMapProvider/MvcSiteMapProvider/Xml/MvcSiteMapSchema.xsd diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/IMvcContextFactory.cs b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/IMvcContextFactory.cs index d9299898..03669e90 100644 --- a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/IMvcContextFactory.cs +++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/IMvcContextFactory.cs @@ -13,9 +13,11 @@ namespace MvcSiteMapProvider.Web.Mvc public interface IMvcContextFactory { HttpContextBase CreateHttpContext(); - HttpContextBase CreateHttpContext(ISiteMapNode node, Uri nodeUri, TextWriter writer); + HttpContextBase CreateHttpContext(ISiteMapNode node, Uri uri, TextWriter writer); RequestContext CreateRequestContext(ISiteMapNode node, RouteData routeData); RequestContext CreateRequestContext(); + RequestContext CreateRequestContext(HttpContextBase httpContext); + RequestContext CreateRequestContext(HttpContextBase httpContext, RouteData routeData); ControllerContext CreateControllerContext(RequestContext requestContext, ControllerBase controller); IRequestCache GetRequestCache(); RouteCollection GetRoutes(); diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/MvcContextFactory.cs b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/MvcContextFactory.cs index 0f043ec2..f2caecc4 100644 --- a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/MvcContextFactory.cs +++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/MvcContextFactory.cs @@ -27,16 +27,16 @@ protected virtual HttpContextBase CreateHttpContext(ISiteMapNode node) return new SiteMapHttpContext(HttpContext.Current, node); } - public virtual HttpContextBase CreateHttpContext(ISiteMapNode node, Uri nodeUri, TextWriter writer) + public virtual HttpContextBase CreateHttpContext(ISiteMapNode node, Uri uri, TextWriter writer) { if (node == null) throw new ArgumentNullException("node"); - if (nodeUri == null) - throw new ArgumentNullException("nodeUri"); + if (uri == null) + throw new ArgumentNullException("uri"); if (writer == null) throw new ArgumentNullException("writer"); - var request = new HttpRequest(string.Empty, nodeUri.ToString(), nodeUri.Query); + var request = new HttpRequest(string.Empty, uri.ToString(), uri.Query); var response = new HttpResponse(writer); var httpContext = new HttpContext(request, response); return new SiteMapHttpContext(httpContext, node); @@ -61,6 +61,16 @@ public virtual RequestContext CreateRequestContext() return new RequestContext(httpContext, new RouteData()); } + public virtual RequestContext CreateRequestContext(HttpContextBase httpContext) + { + return new RequestContext(httpContext, new RouteData()); + } + + public virtual RequestContext CreateRequestContext(HttpContextBase httpContext, RouteData routeData) + { + return new RequestContext(httpContext, routeData); + } + public virtual ControllerContext CreateControllerContext(RequestContext requestContext, ControllerBase controller) { if (requestContext == null) @@ -88,13 +98,13 @@ public virtual RouteCollection GetRoutes() public virtual IUrlHelper CreateUrlHelper(RequestContext requestContext) { - return new UrlHelperAdapter(requestContext); + return new UrlHelperAdapter(requestContext, this.GetRoutes()); } public virtual IUrlHelper CreateUrlHelper() { var requestContext = this.CreateRequestContext(); - return new UrlHelperAdapter(requestContext); + return new UrlHelperAdapter(requestContext, this.GetRoutes()); } public virtual AuthorizationContext CreateAuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor) diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/UrlHelperAdapter.cs b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/UrlHelperAdapter.cs index 36596261..2cf5a897 100644 --- a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/UrlHelperAdapter.cs +++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/UrlHelperAdapter.cs @@ -12,12 +12,12 @@ namespace MvcSiteMapProvider.Web public class UrlHelperAdapter : UrlHelper, IUrlHelper { - public UrlHelperAdapter(RequestContext requestContext) + private UrlHelperAdapter(RequestContext requestContext) : base(requestContext) { } - private UrlHelperAdapter(RequestContext requestContext, RouteCollection routeCollection) + public UrlHelperAdapter(RequestContext requestContext, RouteCollection routeCollection) : base(requestContext, routeCollection) { } diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/UrlResolver/SiteMapNodeUrlResolver.cs b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/UrlResolver/SiteMapNodeUrlResolver.cs index fb9a074f..4f5461e8 100644 --- a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/UrlResolver/SiteMapNodeUrlResolver.cs +++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/UrlResolver/SiteMapNodeUrlResolver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Web; using System.Web.Routing; using MvcSiteMapProvider.Web.Mvc; @@ -42,7 +43,7 @@ IUrlPath urlPath /// The resolved URL. public override string ResolveUrl(ISiteMapNode node, string area, string controller, string action, IDictionary routeValues) { - if (!String.IsNullOrEmpty(node.UnresolvedUrl)) + if (!string.IsNullOrEmpty(node.UnresolvedUrl)) { return this.ResolveVirtualPath(node); } @@ -63,8 +64,28 @@ protected virtual string ResolveVirtualPath(ISiteMapNode node) protected virtual string ResolveRouteUrl(ISiteMapNode node, string area, string controller, string action, IDictionary routeValues) { - string result = String.Empty; - var urlHelper = mvcContextFactory.CreateUrlHelper(); + string result = string.Empty; + // Create a TextWriter with null stream as a backing stream + // which doesn't consume resources + using (var nullWriter = new StreamWriter(Stream.Null)) + { + var requestContext = this.CreateRequestContext(node, true, nullWriter); + result = this.ResolveRouteUrl(node, area, controller, action, routeValues, requestContext); + } + + if (string.IsNullOrEmpty(result)) + { + // fixes #115 - UrlResolver should not throw exception. + return urlPath.MakeVirtualPathAppAbsolute(urlPath.Combine(urlPath.AppDomainAppVirtualPath, "~/")) + Guid.NewGuid().ToString(); + } + + return result; + } + + protected virtual string ResolveRouteUrl(ISiteMapNode node, string area, string controller, string action, IDictionary routeValues, RequestContext requestContext) + { + string result = string.Empty; + var urlHelper = this.mvcContextFactory.CreateUrlHelper(requestContext); var routeValueDictionary = new RouteValueDictionary(routeValues); if (!string.IsNullOrEmpty(node.Route)) @@ -77,13 +98,29 @@ protected virtual string ResolveRouteUrl(ISiteMapNode node, string area, string result = urlHelper.Action(action, controller, routeValueDictionary); } - if (string.IsNullOrEmpty(result)) + return result; + } + + protected virtual HttpContextBase CreateHttpContext(ISiteMapNode node, TextWriter writer) + { + var currentHttpContext = this.mvcContextFactory.CreateHttpContext(); + + // Create a URI with the home page and no query string values. + var uri = new Uri(currentHttpContext.Request.Url, "/"); + return this.mvcContextFactory.CreateHttpContext(node, uri, writer); + } + + protected virtual RequestContext CreateRequestContext(ISiteMapNode node, bool includeAmbientRequestValues, TextWriter writer) + { + if (!includeAmbientRequestValues) { - // fixes #115 - UrlResolver should not throw exception. - return urlPath.MakeVirtualPathAppAbsolute(urlPath.Combine(urlPath.AppDomainAppVirtualPath, "~/")) + Guid.NewGuid().ToString(); + var httpContext = this.CreateHttpContext(node, writer); + return this.mvcContextFactory.CreateRequestContext(httpContext); + } + else + { + return this.mvcContextFactory.CreateRequestContext(); } - - return result; } } } diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Xml/MvcSiteMapSchema.xsd b/src/MvcSiteMapProvider/MvcSiteMapProvider/Xml/MvcSiteMapSchema.xsd new file mode 100644 index 00000000..2a291612 --- /dev/null +++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Xml/MvcSiteMapSchema.xsd @@ -0,0 +1,319 @@ + + + + + + MvcSiteMapProvider SiteMap File Schema + + + + + + + + + + + + + + + + + + + + + + + + + + + + (autogenerated) The unique identifier for the node. + + + + + + + (autogenerated based on routes) The URL represented by the node. + + + + + + + Can optionally be specified to bind the node URL generation to a specific route. + + + + + + + The title of the node. + + + + + + + Description of the node. + + + + + + + The MVC area for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy. + + + + + + + The MVC controller for the sitemap node. Case-sensitive! If not specified, it will be inherited from a node higher in the hierarchy. + + + + + + + The MVC action method for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy. + + + + + + + Comma-separated list of roles allowed to access the node and its child nodes. + + + + + + + Optional resource key. + + + + + + + Is the node clickable or just a grouping node? Default is true. + + + + + + + Optional. Route parameters that should be inherited from the parent sitemap node. This is not a replacement for the SiteMapPreserveRouteDataAttribute. + + + + + + + A class name implementing MvcSiteMapProvider.IDynamicNodeProvider and providing dynamic nodes for the site map. + + + + + + + Class that will be used to generate URLs for sitemap nodes. + + + + + + + Should the resolved URL be cached (true) or resolved on each request (false)? Default is true. + + + + + + + Example from demo: visibility="SiteMapPathHelper,!*" + + + + + + + + Class that will be used to determine visibility for a sitemap node. + + + + + + + Optional target frame for the node link. + + + + + + + + Optional image to be shown by supported HtmlHelpers. + + + + + + + + Optional preserved route parameter names (= values that will be used from the current request route). + + + + + + + + Optional. The primary URL for what is completely or mostly duplicated content on the current node. + The value may be an absolute URL (in the current site or in an external site), a rooted URL starting with '~/', or a relative URL. + + + + + + + Optional. The key value for the node that contains the primary copy of the content for the current node, if it is largely similar. + + + + + + + + Optional. A space-delimited list of values to apply to the robots meta tag. These will apply to all robots. + + + + + + + + The HTTP method that will be used to check node accessibility. This value is used to select correct controller action if a controller has multiple action accepting different HTTP verbs. Set to * to use HTTP method of current request. + + + + + + + + Optional. Sort order of this node relative to its sibling nodes (whether they are defined in XML or in code). + + + + + + + + + + + + + + + + + + Last modified date for the node. Will be output to sitemaps XML for search engines. + + + + + + + + Change frequency for the node. Will be output to sitemaps XML for search engines. + + + + + + + Update priority for the node. Will be output to sitemaps XML for search engines. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file