diff --git a/src/Umbraco.Tests/Routing/LookupByAliasTests.cs b/src/Umbraco.Tests/Routing/LookupByAliasTests.cs index 2b9f05681a..975ab16cbf 100644 --- a/src/Umbraco.Tests/Routing/LookupByAliasTests.cs +++ b/src/Umbraco.Tests/Routing/LookupByAliasTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Routing public void Lookup_By_Url_Alias(string urlAsString, int nodeMatch) { var routingContext = GetRoutingContext(urlAsString); - var url = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docRequest = new PublishedContentRequest(url, routingContext); var lookup = new LookupByAlias(); diff --git a/src/Umbraco.Tests/Routing/LookupByIdTests.cs b/src/Umbraco.Tests/Routing/LookupByIdTests.cs index 0432c7ac9b..9c9078daff 100644 --- a/src/Umbraco.Tests/Routing/LookupByIdTests.cs +++ b/src/Umbraco.Tests/Routing/LookupByIdTests.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Routing public void Lookup_By_Id(string urlAsString, int nodeMatch) { var routingContext = GetRoutingContext(urlAsString); - var url = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docRequest = new PublishedContentRequest(url, routingContext); var lookup = new LookupByIdPath(); diff --git a/src/Umbraco.Tests/Routing/LookupByNiceUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/LookupByNiceUrlAndTemplateTests.cs index fea585627a..72ad748283 100644 --- a/src/Umbraco.Tests/Routing/LookupByNiceUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/LookupByNiceUrlAndTemplateTests.cs @@ -25,7 +25,7 @@ namespace Umbraco.Tests.Routing var template = Template.MakeNew("test", new User(0)); var altTemplate = Template.MakeNew("blah", new User(0)); var routingContext = GetRoutingContext(urlAsString, template); - var url = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docRequest = new PublishedContentRequest(url, routingContext); var lookup = new LookupByNiceUrlAndTemplate(); diff --git a/src/Umbraco.Tests/Routing/LookupByNiceUrlTests.cs b/src/Umbraco.Tests/Routing/LookupByNiceUrlTests.cs index 0ff27ddce6..8cd7c1fd3e 100644 --- a/src/Umbraco.Tests/Routing/LookupByNiceUrlTests.cs +++ b/src/Umbraco.Tests/Routing/LookupByNiceUrlTests.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Routing public void Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) { var routingContext = GetRoutingContext(urlString); - var url = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docreq = new PublishedContentRequest(url, routingContext); var lookup = new LookupByNiceUrl(); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "true"); @@ -66,7 +66,7 @@ namespace Umbraco.Tests.Routing public void Match_Document_By_Url(string urlString, int expectedId) { var routingContext = GetRoutingContext(urlString); - var url = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docreq = new PublishedContentRequest(url, routingContext); var lookup = new LookupByNiceUrl(); diff --git a/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs index 123992b81f..9a0e708b32 100644 --- a/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs @@ -157,7 +157,7 @@ namespace Umbraco.Tests.Routing ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "true"); var routingContext = GetRoutingContext(url); - var uri = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var uri = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docreq = new PublishedContentRequest(uri, routingContext); // must lookup domain else lookup by url fails @@ -196,7 +196,7 @@ namespace Umbraco.Tests.Routing ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "true"); var routingContext = GetRoutingContext(url); - var uri = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var uri = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docreq = new PublishedContentRequest(uri, routingContext); // must lookup domain else lookup by url fails diff --git a/src/Umbraco.Tests/Routing/LookupByPageIdQueryTests.cs b/src/Umbraco.Tests/Routing/LookupByPageIdQueryTests.cs index 43a490e2a5..00ec7a0565 100644 --- a/src/Umbraco.Tests/Routing/LookupByPageIdQueryTests.cs +++ b/src/Umbraco.Tests/Routing/LookupByPageIdQueryTests.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Routing public void Lookup_By_Page_Id(string urlAsString, int nodeMatch) { var routingContext = GetRoutingContext(urlAsString); - var url = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var url = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docRequest = new PublishedContentRequest(url, routingContext); var lookup = new LookupByPageIdQuery(); diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 7ce816644c..306a02bb5b 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -50,13 +50,13 @@ namespace Umbraco.Tests.Routing var route = RouteTable.Routes["Umbraco_default"]; var routeData = new RouteData() { Route = route }; var routingContext = GetRoutingContext("~/dummy-page", template, routeData); - var docRequest = new PublishedContentRequest(routingContext.UmbracoContext.UmbracoUrl, routingContext) + var docRequest = new PublishedContentRequest(routingContext.UmbracoContext.CleanedUmbracoUrl, routingContext) { PublishedContent = routingContext.PublishedContentStore.GetDocumentById(routingContext.UmbracoContext, 1174), Template = template }; - var handler = new RenderRouteHandler(new TestControllerFactory()); + var handler = new RenderRouteHandler(new TestControllerFactory(), routingContext.UmbracoContext); handler.GetHandlerForRoute(routingContext.UmbracoContext.HttpContext.Request.RequestContext, docRequest); Assert.AreEqual("RenderMvc", routeData.Values["controller"].ToString()); @@ -74,13 +74,13 @@ namespace Umbraco.Tests.Routing var route = RouteTable.Routes["Umbraco_default"]; var routeData = new RouteData() {Route = route}; var routingContext = GetRoutingContext("~/dummy-page", template, routeData); - var docRequest = new PublishedContentRequest(routingContext.UmbracoContext.UmbracoUrl, routingContext) + var docRequest = new PublishedContentRequest(routingContext.UmbracoContext.CleanedUmbracoUrl, routingContext) { PublishedContent = routingContext.PublishedContentStore.GetDocumentById(routingContext.UmbracoContext, 1172), Template = template }; - var handler = new RenderRouteHandler(new TestControllerFactory()); + var handler = new RenderRouteHandler(new TestControllerFactory(), routingContext.UmbracoContext); handler.GetHandlerForRoute(routingContext.UmbracoContext.HttpContext.Request.RequestContext, docRequest); Assert.AreEqual("CustomDocument", routeData.Values["controller"].ToString()); diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index e503f1f71a..c00e97eb15 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -1,17 +1,47 @@ using System; using System.Configuration; using System.IO; +using System.Threading; using System.Xml; using NUnit.Framework; using Umbraco.Core; using Umbraco.Tests.TestHelpers; using Umbraco.Web; +using Umbraco.Web.Routing; +using umbraco.BusinessLogic; using umbraco.IO; using umbraco.cms.businesslogic.cache; using umbraco.cms.businesslogic.template; namespace Umbraco.Tests.Routing { + [TestFixture, RequiresSTA] + public class PublishedContentRequestBuilderTests : BaseRoutingTest + { + + //[Test] + //public void Alt_Template() + //{ + // var template = Template.MakeNew("test", new User(0)); + // var altTemplate = Template.MakeNew("alt", new User(0)); + // var umbracoContext = GetUmbracoContext("/home?altTemplate=" + altTemplate.Alias, template.Id); + // // create the new document request since we're rendering a document on the front-end + // var docreq = PublishedContentRequest.CreateForFrontEndRequest(umbracoContext); + + // //create the searcher + // var searcher = new PublishedContentRequestBuilder(docreq); + // //find domain + // searcher.LookupDomain(); + // // redirect if it has been flagged + // Assert.IsFalse(docreq.IsRedirect); + + // //find the document, found will be true if the doc request has found BOTH a node and a template + // var found = searcher.LookupDocument(); + + //} + + } + [TestFixture, RequiresSTA] public class UmbracoModuleTests : BaseRoutingTest { diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 7f4b68d2bd..cf35c429d7 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -42,7 +42,7 @@ namespace Umbraco.Tests.Routing // route a rogue url url = "http://domain1.com/1001-1/1001-1-1"; - var uri = routingContext.UmbracoContext.UmbracoUrl; //very important to use the cleaned up umbraco url + var uri = routingContext.UmbracoContext.CleanedUmbracoUrl; //very important to use the cleaned up umbraco url var docreq = new PublishedContentRequest(uri, routingContext); var builder = new PublishedContentRequestBuilder(docreq); builder.LookupDomain(); diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 30b036685f..0303609a5b 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -337,7 +337,7 @@ namespace Umbraco.Web Mandate.ParameterNotNullOrEmpty(action, "action"); Mandate.ParameterNotNullOrEmpty(controllerName, "controllerName"); - var formAction = UmbracoContext.Current.RequestUrl.ToString(); + var formAction = UmbracoContext.Current.OriginalRequestUrl.ToString(); return html.RenderForm(formAction, FormMethod.Post, htmlAttributes, controllerName, action, area, additionalRouteVals); } diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index a6fda62175..f0ae3607a6 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -18,10 +18,33 @@ namespace Umbraco.Web.Mvc public RenderRouteHandler(IControllerFactory controllerFactory) { + if (controllerFactory == null) throw new ArgumentNullException("controllerFactory"); _controllerFactory = controllerFactory; } + /// + /// Contructor generally used for unit testing + /// + /// + /// + internal RenderRouteHandler(IControllerFactory controllerFactory, UmbracoContext umbracoContext) + { + if (controllerFactory == null) throw new ArgumentNullException("controllerFactory"); + if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); + _controllerFactory = controllerFactory; + _umbracoContext = umbracoContext; + } + private readonly IControllerFactory _controllerFactory; + private readonly UmbracoContext _umbracoContext; + + /// + /// Returns the current UmbracoContext + /// + public UmbracoContext UmbracoContext + { + get { return _umbracoContext ?? UmbracoContext.Current; } + } #region IRouteHandler Members @@ -33,11 +56,11 @@ namespace Umbraco.Web.Mvc /// public IHttpHandler GetHttpHandler(RequestContext requestContext) { - if (UmbracoContext.Current == null) - { + if (UmbracoContext == null) + { throw new NullReferenceException("There is not current UmbracoContext, it must be initialized before the RenderRouteHandler executes"); } - var docRequest = UmbracoContext.Current.PublishedContentRequest; + var docRequest = UmbracoContext.PublishedContentRequest; if (docRequest == null) { throw new NullReferenceException("There is not current PublishedContentRequest, it must be initialized before the RenderRouteHandler executes"); @@ -52,7 +75,7 @@ namespace Umbraco.Web.Mvc //put essential data into the data tokens, the 'umbraco' key is required to be there for the view engine requestContext.RouteData.DataTokens.Add("umbraco", renderModel); //required for the RenderModelBinder requestContext.RouteData.DataTokens.Add("umbraco-doc-request", docRequest); //required for RenderMvcController - requestContext.RouteData.DataTokens.Add("umbraco-context", UmbracoContext.Current); //required for UmbracoTemplatePage + requestContext.RouteData.DataTokens.Add("umbraco-context", UmbracoContext); //required for UmbracoTemplatePage return GetHandlerForRoute(requestContext, docRequest); @@ -256,6 +279,11 @@ namespace Umbraco.Web.Mvc { requestContext.RouteData.Values["action"] = routeDef.ActionName; } + + // reset the friendly path so in the controllers and anything occuring after this point in time, + //the URL is reset back to the original request. + requestContext.HttpContext.RewritePath(UmbracoContext.OriginalRequestUrl.PathAndQuery); + return new MvcHandler(requestContext); } } diff --git a/src/Umbraco.Web/Routing/NiceUrlProvider.cs b/src/Umbraco.Web/Routing/NiceUrlProvider.cs index b11bd4ae26..4bf2df22a1 100644 --- a/src/Umbraco.Web/Routing/NiceUrlProvider.cs +++ b/src/Umbraco.Web/Routing/NiceUrlProvider.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.Routing public string GetNiceUrl(int nodeId) { var absolute = UmbracoSettings.UseDomainPrefixes || this.EnforceAbsoluteUrls; - return GetNiceUrl(nodeId, _umbracoContext.UmbracoUrl, absolute); + return GetNiceUrl(nodeId, _umbracoContext.CleanedUmbracoUrl, absolute); } /// @@ -60,7 +60,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on the current url, unless absolute is true, in which case the url is always absolute. public string GetNiceUrl(int nodeId, bool absolute) { - return GetNiceUrl(nodeId, _umbracoContext.UmbracoUrl, absolute); + return GetNiceUrl(nodeId, _umbracoContext.CleanedUmbracoUrl, absolute); } /// @@ -145,7 +145,7 @@ namespace Umbraco.Web.Routing public IEnumerable GetAllAbsoluteNiceUrls(int nodeId) { - return GetAllAbsoluteNiceUrls(nodeId, _umbracoContext.UmbracoUrl); + return GetAllAbsoluteNiceUrls(nodeId, _umbracoContext.CleanedUmbracoUrl); } /// diff --git a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs index cda85667a2..1259b22fdb 100644 --- a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs +++ b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs @@ -27,7 +27,7 @@ namespace Umbraco.Web.Routing response.Write("

Page not found

"); response.Write("

"); - response.Write(string.Format(reason, HttpUtility.HtmlEncode(UmbracoContext.Current.ClientUrl))); + response.Write(string.Format(reason, HttpUtility.HtmlEncode(UmbracoContext.Current.OriginalRequestUrl))); response.Write("This page can be replaced with a custom 404. Check the documentation for \"custom 404\".

"); response.Write("

This page is intentionally left ugly ;-)

"); diff --git a/src/Umbraco.Web/Routing/PublishedContentRequest.cs b/src/Umbraco.Web/Routing/PublishedContentRequest.cs index 3d4918d396..d3bfd1e0ef 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequest.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequest.cs @@ -1,6 +1,8 @@ using System; using System.Linq; using System.Text; +using System.Threading; +using System.Web; using System.Xml; using System.Globalization; using System.Diagnostics; @@ -25,10 +27,108 @@ namespace Umbraco.Web.Routing ///

internal class PublishedContentRequest { + + /// + /// This creates a PublishedContentRequest and assigns it to the current HttpContext and then proceeds to + /// process the request using the PublishedContentRequestBuilder. If everything is successful, the callback + /// method will be called. + /// + /// + /// + /// + /// + internal static void ProcessRequest(HttpContextBase httpContext, UmbracoContext umbracoContext, Uri uri, Action onSuccess) + { + if (umbracoContext == null) + throw new NullReferenceException("The UmbracoContext.Current is null, ProcessRequest cannot proceed unless there is a current UmbracoContext"); + if (uri == null) throw new ArgumentNullException("uri"); + if (umbracoContext.RoutingContext == null) + throw new NullReferenceException("The UmbracoContext.RoutingContext has not been assigned, ProcessRequest cannot proceed unless there is a RoutingContext assigned to the UmbracoContext"); + + var docreq = new PublishedContentRequest(uri, umbracoContext.RoutingContext); + //assign back since this is a front-end request + umbracoContext.PublishedContentRequest = docreq; + + // note - at that point the original legacy module did something do handle IIS custom 404 errors + // ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support + // "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain + // to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk. + // + // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors + // so that they point to a non-existing page eg /redirect-404.aspx + // TODO: SD: We need more information on this for when we release 4.10.0 as I'm not sure what this means. + + //create the searcher + var searcher = new PublishedContentRequestBuilder(docreq); + //find domain + searcher.LookupDomain(); + // redirect if it has been flagged + if (docreq.IsRedirect) + httpContext.Response.Redirect(docreq.RedirectUrl, true); + //set the culture on the thread + Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = docreq.Culture; + //find the document, found will be true if the doc request has found BOTH a node and a template + // though currently we don't use this value. + var found = searcher.LookupDocument(); + //this could be called in the LookupDocument method, but I've just put it here for clarity. + searcher.DetermineRenderingEngine(); + + //TODO: here we should launch an event so that people can modify the doc request to do whatever they want. + + // redirect if it has been flagged + if (docreq.IsRedirect) + httpContext.Response.Redirect(docreq.RedirectUrl, true); + + // handle 404 + if (docreq.Is404) + { + httpContext.Response.StatusCode = 404; + + if (!docreq.HasNode) + { + httpContext.RemapHandler(new PublishedContentNotFoundHandler()); + return; + } + + // else we have a document to render + // not having a template is ok here, MVC will take care of it + } + + // just be safe - should never ever happen + if (!docreq.HasNode) + throw new Exception("No document to render."); + + // render even though we might have no template + // to give MVC a chance to hijack routes + // pass off to our handlers (mvc or webforms) + + // assign the legacy page back to the docrequest + // handlers like default.aspx will want it and most macros currently need it + docreq.UmbracoPage = new page(docreq); + + // these two are used by many legacy objects + httpContext.Items["pageID"] = docreq.DocumentId; + httpContext.Items["pageElements"] = docreq.UmbracoPage.Elements; + + if (onSuccess != null) + onSuccess(docreq); + } + + + /// + /// Create a content request for a specific URL + /// + /// + /// public PublishedContentRequest(Uri uri, RoutingContext routingContext) { + if (uri == null) throw new ArgumentNullException("uri"); + if (routingContext == null) throw new ArgumentNullException("routingContext"); + this.Uri = uri; RoutingContext = routingContext; + + //set default RenderingEngine = RenderingEngine.Mvc; } diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs index e8cfbd856b..4c0b61214d 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs @@ -100,7 +100,7 @@ namespace Umbraco.Web.Routing LogHelper.Debug("{0}Uri=\"{1}\"", () => tracePrefix, () => _publishedContentRequest.Uri); // try to find a domain matching the current request - var domainAndUri = DomainHelper.DomainMatch(Domain.GetDomains(), _umbracoContext.UmbracoUrl, false); + var domainAndUri = DomainHelper.DomainMatch(Domain.GetDomains(), _umbracoContext.CleanedUmbracoUrl, false); // handle domain if (domainAndUri != null) diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index a0ff73230b..caa62575a9 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -51,21 +51,12 @@ namespace Umbraco.Web Application = applicationContext; RoutesCache = routesCache; - // set the urls - this.RequestUrl = httpContext.Request.Url; - //RawUrl gets preserved across rewrites - this.ClientUrl = UriUtility.ToFullUrl(httpContext.Request.RawUrl, httpContext); - - //// from IIS Rewrite Module documentation: - //// "The URL Rewrite Module preserves the original requested URL path in the following server variables: - //// - HTTP_X_ORIGINAL_URL – this server variable contains the original URL in decoded format; - //// - UNENCODED_URL – this server variable contains the original URL exactly as it was requested by a Web client, with all original encoding preserved." - //// - //// see also http://forums.iis.net/t/1152581.aspx - //// - //var iisOrig = httpContext.Request.ServerVariables["HTTP_X_ORIGINAL_URL"]; - //if (!string.IsNullOrWhiteSpace(iisOrig)) - // this.ClientUrl = new Uri(iisOrig); + // set the urls... + //original request url + this.OriginalRequestUrl = httpContext.Request.Url; + //cleaned request url + this.CleanedUmbracoUrl = UriUtility.UriToUmbraco(this.OriginalRequestUrl); + } /// @@ -114,35 +105,17 @@ namespace Umbraco.Web public ApplicationContext Application { get; private set; } internal IRoutesCache RoutesCache { get; private set; } - - /// - /// Gets/sets the original URL of the request - /// - internal Uri ClientUrl { get; set; } - - private Uri _requestUrl; - - /// - /// Gets the uri that is handled by ASP.NET after server-side rewriting took place. - /// - internal Uri RequestUrl - { - get - { - return _requestUrl; - } - set - { - _requestUrl = value; - this.UmbracoUrl = UriUtility.UriToUmbraco(_requestUrl); - } - } + + /// + /// Gets the uri that is handled by ASP.NET after server-side rewriting took place. + /// + internal Uri OriginalRequestUrl { get; private set; } /// /// Gets the cleaned up url that is handled by Umbraco. /// /// That is, lowercase, no trailing slash after path, no .aspx... - internal Uri UmbracoUrl { get; private set; } + internal Uri CleanedUmbracoUrl { get; private set; } private Func _xmlDelegate; diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 8368563990..0a84dbf9f0 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -37,7 +37,7 @@ namespace Umbraco.Web { // do not process if client-side request if (IsClientSideRequest(httpContext.Request.Url)) - return; + return; // ok, process @@ -76,11 +76,15 @@ namespace Umbraco.Web if (IsClientSideRequest(httpContext.Request.Url)) return; - var umbracoContext = UmbracoContext.Current; - var routingContext = umbracoContext.RoutingContext; + if (UmbracoContext.Current == null) + throw new NullReferenceException("The UmbracoContext.Current is null, ProcessRequest cannot proceed unless there is a current UmbracoContext"); + if (UmbracoContext.Current.RoutingContext == null) + throw new NullReferenceException("The UmbracoContext.RoutingContext has not been assigned, ProcessRequest cannot proceed unless there is a RoutingContext assigned to the UmbracoContext"); + + var umbracoContext = UmbracoContext.Current; // do not process but remap to handler if it is a base rest request - if (BaseRest.BaseRestHandler.IsBaseRestRequest(umbracoContext.RequestUrl)) + if (BaseRest.BaseRestHandler.IsBaseRestRequest(umbracoContext.OriginalRequestUrl)) { httpContext.RemapHandler(new BaseRest.BaseRestHandler()); return; @@ -92,80 +96,17 @@ namespace Umbraco.Web // ok, process - var uri = umbracoContext.RequestUrl; + var uri = umbracoContext.OriginalRequestUrl; - // legacy - no idea what this is + // legacy - no idea what this is but does something with the query strings LegacyCleanUmbPageFromQueryString(ref uri); - // create the new document request since we're rendering a document on the front-end - var docreq = new PublishedContentRequest( - umbracoContext.UmbracoUrl, //very important to use this url! it is the path only lowercased version of the current URL. - routingContext); - //assign the document request to the umbraco context now that we know its a front end request - umbracoContext.PublishedContentRequest = docreq; - - // note - at that point the original legacy module did something do handle IIS custom 404 errors - // ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support - // "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain - // to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk. - // - // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors - // so that they point to a non-existing page eg /redirect-404.aspx - // TODO: SD: We need more information on this for when we release 4.10.0 as I'm not sure what this means. - - //create the searcher - var searcher = new PublishedContentRequestBuilder(docreq); - //find domain - searcher.LookupDomain(); - // redirect if it has been flagged - if (docreq.IsRedirect) - httpContext.Response.Redirect(docreq.RedirectUrl, true); - //set the culture on the thread - Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = docreq.Culture; - //find the document, found will be true if the doc request has found BOTH a node and a template - // though currently we don't use this value. - var found = searcher.LookupDocument(); - //this could be called in the LookupDocument method, but I've just put it here for clarity. - searcher.DetermineRenderingEngine(); - - //TODO: here we should launch an event so that people can modify the doc request to do whatever they want. - - // redirect if it has been flagged - if (docreq.IsRedirect) - httpContext.Response.Redirect(docreq.RedirectUrl, true); - - // handle 404 - if (docreq.Is404) - { - httpContext.Response.StatusCode = 404; - - if (!docreq.HasNode) - { - httpContext.RemapHandler(new PublishedContentNotFoundHandler()); - return; - } - - // else we have a document to render - // not having a template is ok here, MVC will take care of it - } - - // just be safe - should never ever happen - if (!docreq.HasNode) - throw new Exception("No document to render."); - - // render even though we might have no template - // to give MVC a chance to hijack routes - // pass off to our handlers (mvc or webforms) - - // assign the legacy page back to the docrequest - // handlers like default.aspx will want it and most macros currently need it - docreq.UmbracoPage = new page(docreq); - - // these two are used by many legacy objects - httpContext.Items["pageID"] = docreq.DocumentId; - httpContext.Items["pageElements"] = docreq.UmbracoPage.Elements; - - RewriteToUmbracoHandler(HttpContext.Current, uri.Query, docreq.RenderingEngine); + //process the request and on success, call the callback method: RewriteToUmbracoHandler + PublishedContentRequest.ProcessRequest( + httpContext, + umbracoContext, + umbracoContext.CleanedUmbracoUrl, //very important to use this url! it is the path only lowercased version of the current URL. + docreq => RewriteToUmbracoHandler(HttpContext.Current, uri.Query, docreq.RenderingEngine)); } /// @@ -210,7 +151,7 @@ namespace Umbraco.Web /// internal bool EnsureUmbracoRoutablePage(UmbracoContext context, HttpContextBase httpContext) { - var uri = context.RequestUrl; + var uri = context.OriginalRequestUrl; // ensure this is a document request if (!EnsureDocumentRequest(httpContext, uri)) @@ -378,7 +319,7 @@ namespace Umbraco.Web //then we should be rendering the MVC stuff in that location. rewritePath = "~/" + GlobalSettings.Path.TrimStart(new[] { '~', '/' }).TrimEnd(new[] { '/' }) - + "/RenderMvc" + currentQuery; + + "/RenderMvc"; //First we rewrite the path to the path of the handler (i.e. default.aspx or /umbraco/RenderMvc ) context.RewritePath(rewritePath, "", currentQuery.TrimStart(new[] { '?' }), false); @@ -388,14 +329,17 @@ namespace Umbraco.Web //an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does: // * Looks up the route based on the new rewritten URL // * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route - //we could have re-written this functionality, but the code inside the PostResolveRequestCache is exactly what we want. + //we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal + //so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink + //a bunch of things! + var urlRouting = new UrlRoutingModule(); urlRouting.PostResolveRequestCache(new HttpContextWrapper(context)); break; case RenderingEngine.WebForms: default: - rewritePath = "~/default.aspx" + currentQuery; + rewritePath = "~/default.aspx"; //First we rewrite the path to the path of the handler (i.e. default.aspx or /umbraco/RenderMvc ) context.RewritePath(rewritePath, "", currentQuery.TrimStart(new[] { '?' }), false); diff --git a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs index 7a73e510f6..273f135793 100644 --- a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs @@ -65,7 +65,7 @@ namespace umbraco this.MasterPageFile = template.GetMasterPageName(_upage.Template); // reset the friendly path so it's used by forms, etc. - Context.RewritePath(UmbracoContext.Current.RequestUrl.PathAndQuery); + Context.RewritePath(UmbracoContext.Current.OriginalRequestUrl.PathAndQuery); //fire the init finished event FireAfterRequestInit(args); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/uQuery/uQuery-Nodes.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/uQuery/uQuery-Nodes.cs index 65c1913442..371357fc47 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/uQuery/uQuery-Nodes.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/uQuery/uQuery-Nodes.cs @@ -204,7 +204,7 @@ namespace umbraco { var uri = new Uri(url, UriKind.RelativeOrAbsolute); if (!uri.IsAbsoluteUri) - uri = uri.MakeAbsolute(Umbraco.Web.UmbracoContext.Current.UmbracoUrl); + uri = uri.MakeAbsolute(Umbraco.Web.UmbracoContext.Current.CleanedUmbracoUrl); uri = Umbraco.Web.UriUtility.UriToUmbraco(uri); var docreq = new Umbraco.Web.Routing.PublishedContentRequest(uri, Umbraco.Web.UmbracoContext.Current.RoutingContext);