diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
index f0ae3607a6..6069060648 100644
--- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
+++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
@@ -269,7 +269,21 @@ namespace Umbraco.Web.Mvc
//we want to return a blank page, but we'll leave that up to the NoTemplateHandler.
if (!publishedContentRequest.HasTemplate && !routeDef.HasHijackedRoute)
{
- return new NoTemplateHandler();
+ var handler = publishedContentRequest.ProcessNoTemplateInMvc(requestContext.HttpContext);
+
+ // if it's not null it can be either the PublishedContentNotFoundHandler (no document was
+ // found to handle 404, or document with no template was found) or the WebForms handler
+ // (a document was found and its template is WebForms)
+
+ // if it's null it means that a document was found and its template is Mvc
+
+ // if we have a handler, return now
+ if (handler != null)
+ return handler;
+
+ // else we are running Mvc
+ // update the route definition
+ routeDef = GetUmbracoRouteDefinition(requestContext, publishedContentRequest);
}
//no post values, just route to the controller/action requried (local)
diff --git a/src/Umbraco.Web/Routing/NoTemplateHandler.cs b/src/Umbraco.Web/Routing/NoTemplateHandler.cs
deleted file mode 100644
index 8131d0f195..0000000000
--- a/src/Umbraco.Web/Routing/NoTemplateHandler.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Web;
-
-namespace Umbraco.Web.Routing
-{
- ///
- /// Gets executed when there is no template assigned to a request and there is no hijacked MVC route
- ///
- internal class NoTemplateHandler : IHttpHandler
- {
- public void ProcessRequest(HttpContext context)
- {
- WriteOutput(context);
- }
-
- internal void WriteOutput(HttpContext context)
- {
- var response = context.Response;
-
- response.Clear();
- response.Write("");
- response.End();
- }
-
- public bool IsReusable
- {
- get { return true; }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs
index 1259b22fdb..282f118899 100644
--- a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs
+++ b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs
@@ -7,6 +7,16 @@ namespace Umbraco.Web.Routing
///
internal class PublishedContentNotFoundHandler : IHttpHandler
{
+ string _message;
+
+ public PublishedContentNotFoundHandler()
+ { }
+
+ public PublishedContentNotFoundHandler(string message)
+ {
+ _message = message;
+ }
+
public void ProcessRequest(HttpContext context)
{
WriteOutput(context);
@@ -28,7 +38,9 @@ namespace Umbraco.Web.Routing
response.Write("
Page not found
");
response.Write("");
response.Write(string.Format(reason, HttpUtility.HtmlEncode(UmbracoContext.Current.OriginalRequestUrl)));
- response.Write("
");
+ if (!string.IsNullOrWhiteSpace(_message))
+ response.Write("" + _message + "
");
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 ;-)
");
response.Write("");
@@ -38,7 +50,7 @@ namespace Umbraco.Web.Routing
public bool IsReusable
{
- get { return true; }
+ get { return false; }
}
}
}
diff --git a/src/Umbraco.Web/Routing/PublishedContentRequest.cs b/src/Umbraco.Web/Routing/PublishedContentRequest.cs
index 897623e4e9..3d513cd06f 100644
--- a/src/Umbraco.Web/Routing/PublishedContentRequest.cs
+++ b/src/Umbraco.Web/Routing/PublishedContentRequest.cs
@@ -28,27 +28,22 @@ 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.
+ /// Assigns the request to the http context and proceeds to process the request. If everything is successful, invoke the callback.
///
///
///
///
///
- internal static void ProcessRequest(HttpContextBase httpContext, UmbracoContext umbracoContext, Uri uri, Action onSuccess)
+ internal void ProcessRequest(HttpContextBase httpContext, UmbracoContext umbracoContext, 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;
+ umbracoContext.PublishedContentRequest = this;
// 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
@@ -59,33 +54,31 @@ namespace Umbraco.Web.Routing
// 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();
+ _builder.LookupDomain();
// redirect if it has been flagged
- if (docreq.IsRedirect)
- httpContext.Response.Redirect(docreq.RedirectUrl, true);
+ if (this.IsRedirect)
+ httpContext.Response.Redirect(this.RedirectUrl, true);
//set the culture on the thread
- Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = docreq.Culture;
+ Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = this.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();
+ var found = _builder.LookupDocument();
//this could be called in the LookupDocument method, but I've just put it here for clarity.
- searcher.DetermineRenderingEngine();
+ _builder.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);
+ if (this.IsRedirect)
+ httpContext.Response.Redirect(this.RedirectUrl, true);
// handle 404
- if (docreq.Is404)
+ if (this.Is404)
{
httpContext.Response.StatusCode = 404;
- if (!docreq.HasNode)
+ if (!this.HasNode)
{
httpContext.RemapHandler(new PublishedContentNotFoundHandler());
return;
@@ -96,7 +89,7 @@ namespace Umbraco.Web.Routing
}
// just be safe - should never ever happen
- if (!docreq.HasNode)
+ if (!this.HasNode)
throw new Exception("No document to render.");
// render even though we might have no template
@@ -105,67 +98,135 @@ namespace Umbraco.Web.Routing
// 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);
+ this.UmbracoPage = new page(this);
// these two are used by many legacy objects
- httpContext.Items["pageID"] = docreq.DocumentId;
- httpContext.Items["pageElements"] = docreq.UmbracoPage.Elements;
+ httpContext.Items["pageID"] = this.DocumentId;
+ httpContext.Items["pageElements"] = this.UmbracoPage.Elements;
if (onSuccess != null)
- onSuccess(docreq);
+ onSuccess(this);
}
+ internal IHttpHandler ProcessNoTemplateInMvc(HttpContextBase httpContext)
+ {
+ var content = this.PublishedContent;
+ this.PublishedContent = null;
+
+ _builder.LookupDocument2();
+ _builder.DetermineRenderingEngine();
+
+ // redirect if it has been flagged
+ if (this.IsRedirect)
+ httpContext.Response.Redirect(this.RedirectUrl, true);
+
+ // here .Is404 _has_ to be true
+ httpContext.Response.StatusCode = 404;
+
+ if (!this.HasNode)
+ {
+ // means the builder could not find a proper document to handle 404
+ // restore the saved content so we know it exists
+ this.PublishedContent = content;
+ return new PublishedContentNotFoundHandler();
+ }
+
+ if (!this.HasTemplate)
+ {
+ // means the builder could find a proper document, but the document has no template
+ // at that point there isn't much we can do and there is no point returning
+ // to Mvc since Mvc can't do much
+ return new PublishedContentNotFoundHandler("In addition, no template exists to render the custom 404.");
+ }
+
+ // 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
+ this.UmbracoPage = new page(this);
+
+ // these two are used by many legacy objects
+ httpContext.Items["pageID"] = this.DocumentId;
+ httpContext.Items["pageElements"] = this.UmbracoPage.Elements;
+
+ switch (this.RenderingEngine)
+ {
+ case Core.RenderingEngine.Mvc:
+ return null;
+ case Core.RenderingEngine.WebForms:
+ default:
+ return (global::umbraco.UmbracoDefault)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault));
+ }
+ }
+
+ private PublishedContentRequestBuilder _builder;
///
- /// Create a content request for a specific URL
+ /// Initializes a new instance of the class with a specific Uri and routing context.
///
- ///
- ///
+ /// The request Uri.
+ /// A routing context.
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;
- }
+ this.RoutingContext = routingContext;
- ///
- /// the id of the requested node, if any, else zero.
- ///
- int _nodeId = 0;
-
- private IPublishedContent _publishedContent = null;
+ _builder = new PublishedContentRequestBuilder(this);
+
+ // set default
+ this.RenderingEngine = RenderingEngine.Mvc;
+ }
#region Properties
///
- /// Returns the current RoutingContext
+ /// The identifier of the requested node, if any, else zero.
+ ///
+ int _nodeId = 0;
+
+ ///
+ /// The requested node, if any, else null.
+ ///
+ private IPublishedContent _publishedContent = null;
+
+ ///
+ /// The "umbraco page" object.
+ ///
+ private page _umbracoPage;
+
+ ///
+ /// Gets or sets the current RoutingContext.
///
public RoutingContext RoutingContext { get; private set; }
///
- /// The cleaned up Uri used for routing
+ /// Gets or sets the cleaned up Uri used for routing.
///
public Uri Uri { get; private set; }
///
- /// Gets or sets the document request's domain.
+ /// Gets or sets the content request's domain.
///
public Domain Domain { get; internal set; }
+ ///
+ /// Gets or sets the content request's domain Uri.
+ ///
+ /// The Domain may contain "example.com" whereas the Uri will be fully qualified eg "http://example.com/".
public Uri DomainUri { get; internal set; }
///
- /// Gets or sets whether the rendering engine is MVC or WebForms
+ /// Gets or sets whether the rendering engine is MVC or WebForms.
///
public RenderingEngine RenderingEngine { get; internal set; }
///
- /// Gets a value indicating whether the document request has a domain.
+ /// Gets a value indicating whether the content request has a domain.
///
public bool HasDomain
{
@@ -173,14 +234,12 @@ namespace Umbraco.Web.Routing
}
///
- /// Gets or sets the document request's culture
+ /// Gets or sets the content request's culture.
///
public CultureInfo Culture { get; set; }
- private page _umbracoPage;
-
///
- /// Returns the Umbraco page object
+ /// Gets or sets the "umbraco page" object.
///
///
/// This value is only used for legacy/webforms code.
@@ -190,9 +249,8 @@ namespace Umbraco.Web.Routing
get
{
if (_umbracoPage == null)
- {
throw new InvalidOperationException("The umbraco page object is only available once Finalize()");
- }
+
return _umbracoPage;
}
set { _umbracoPage = value; }
@@ -201,6 +259,9 @@ namespace Umbraco.Web.Routing
// TODO: fixme - do we want to have an ordered list of alternate cultures,
// to allow for fallbacks when doing dictionnary lookup and such?
+ ///
+ /// Gets or sets the requested content.
+ ///
public IPublishedContent PublishedContent
{
get { return _publishedContent; }
@@ -213,12 +274,12 @@ namespace Umbraco.Web.Routing
}
///
- /// Gets or sets the document request's template lookup
+ /// Gets or sets the template to use to display the requested content.
///
public Template Template { get; set; }
///
- /// Gets a value indicating whether the document request has a template.
+ /// Gets a value indicating whether the content request has a template.
///
public bool HasTemplate
{
@@ -226,9 +287,9 @@ namespace Umbraco.Web.Routing
}
///
- /// Gets the id of the document.
+ /// Gets the identifier of the requested content.
///
- /// Thrown when the document request has no document.
+ /// Thrown when the content request has no content.
public int DocumentId
{
get
@@ -240,7 +301,7 @@ namespace Umbraco.Web.Routing
}
///
- /// Gets a value indicating whether the document request has a document.
+ /// Gets a value indicating whether the content request has a content.
///
public bool HasNode
{
@@ -248,21 +309,21 @@ namespace Umbraco.Web.Routing
}
///
- /// Gets or sets a value indicating whether the requested document could not be found. This is set in the PublishedContentRequestBuilder.
+ /// Gets or sets a value indicating whether the requested content could not be found.
///
+ /// This is set in the PublishedContentRequestBuilder.
internal bool Is404 { get; set; }
///
- /// Gets a value indicating whether the document request triggers a redirect.
+ /// Gets a value indicating whether the content request triggers a redirect.
///
public bool IsRedirect { get { return !string.IsNullOrWhiteSpace(this.RedirectUrl); } }
///
- /// Gets the url to redirect to, when the document request triggers a redirect.
+ /// Gets or sets the url to redirect to, when the content request triggers a redirect.
///
public string RedirectUrl { get; set; }
- #endregion
-
+ #endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs
index 4c0b61214d..6b8025cd29 100644
--- a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs
+++ b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs
@@ -175,7 +175,7 @@ namespace Umbraco.Web.Routing
/// Performs the document resolution second pass.
///
/// The second pass consists in handling "not found", internal redirects, access validation, and template.
- private void LookupDocument2()
+ internal void LookupDocument2()
{
const string tracePrefix = "LookupDocument2: ";
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 528e295273..9f3080b756 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -313,7 +313,6 @@
-
diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs
index 0a84dbf9f0..4b3367ef07 100644
--- a/src/Umbraco.Web/UmbracoModule.cs
+++ b/src/Umbraco.Web/UmbracoModule.cs
@@ -101,12 +101,10 @@ namespace Umbraco.Web
// legacy - no idea what this is but does something with the query strings
LegacyCleanUmbPageFromQueryString(ref uri);
- //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));
+ // instanciate a request a process
+ // important to use CleanedUmbracoUrl - lowercase path-only version of the current url
+ var docreq = new PublishedContentRequest(umbracoContext.CleanedUmbracoUrl, umbracoContext.RoutingContext);
+ docreq.ProcessRequest(httpContext, umbracoContext, docreq2 => RewriteToUmbracoHandler(HttpContext.Current, uri.Query, docreq2.RenderingEngine));
}
///
@@ -320,7 +318,7 @@ namespace Umbraco.Web
rewritePath = "~/"
+ GlobalSettings.Path.TrimStart(new[] { '~', '/' }).TrimEnd(new[] { '/' })
+ "/RenderMvc";
- //First we rewrite the path to the path of the handler (i.e. default.aspx or /umbraco/RenderMvc )
+ // we rewrite the path to the path of the handler (i.e. default.aspx or /umbraco/RenderMvc )
context.RewritePath(rewritePath, "", currentQuery.TrimStart(new[] { '?' }), false);
//if it is MVC we need to do something special, we are not using TransferRequest as this will
@@ -340,12 +338,9 @@ namespace Umbraco.Web
case RenderingEngine.WebForms:
default:
rewritePath = "~/default.aspx";
- //First we rewrite the path to the path of the handler (i.e. default.aspx or /umbraco/RenderMvc )
+ // rewrite the path to the path of the handler (i.e. default.aspx or /umbraco/RenderMvc )
context.RewritePath(rewritePath, "", currentQuery.TrimStart(new[] { '?' }), false);
- //now, execute the handler
- var webFormshandler = (global::umbraco.UmbracoDefault)BuildManager.CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault));
- context.RemapHandler(webFormshandler);
break;
}