Trouble rendering partial views from RenderMvcController
Note: This is all against 6.1 tip as of Apr10. Moving conversation here per Stephan's request.
I'm trying to do something a little unorthodox and running into some issues. I'm trying to render some of my partial views (nav, footer) to an external endpoint so they can be shared with some WordPress blogs. I created a simple RenderMvcController to handle this.
public class ExternalContentController : mvc.RenderMvcController
{
public ActionResult Content(string viewName)
{
return PartialView(viewName);
}
}
With only this in place a request to say, /external/navMenu, will throw an exception inside the view if it references UmbracoContext because there is no PublishedContentRequest. I tried to fix this with a ContentFinder that would just set the the PCR to the site homepage if the controller name was "ExternalContent", but then I found that the finders are never even called because the route fails the EnsureUmbracoRoutablePage check in UmbracoModule.ProcessRequest.
I'm guessing the traditional (4.x, but assuming MVC was there;)) Umbraco way to do this would have been to have expose the partials in a content node with the partial as its template, but that feels pretty dirty to me. I also think I might run into issues later if the partials have to be in /views and can't be under /views/shared.
How do you plumb everything so that ExternalContentController runs? Do you register routes? Have you declared "external" as a reserved path?
When you say it fails the EnsureUmbracoRoutablePage... how do you know? Have you stepped through the code? Do you have an UmbracoRoutableOutcome reason that would explain why it failed?
When you say it throws an exception... can you post the exact stack trace here?
Yes, the routes are wired up in my application, which is why my action gets called. Stepping thru a little bit more, it looks like GlobalSettings.IsReservedPathOrUrl returns true as expected, which causes EnsureDocumentRequest to be false.
If the partial in question is HTML-only, everything works just fine. The exception comes when referencing the UmbracoContext or Helper in the partial. If I reference the Umbraco helper first, I get ...
[NullReferenceException: Object reference not set to an instance of an object.]
Umbraco.Web.Mvc.UmbracoTemplatePage.get_Umbraco() in \src\Umbraco.Web\Mvc\UmbracoTemplatePage.cs:65
ASP._Page_Views_Shared_navMenu_cshtml.Execute() in DMag.Umbraco.Web\Views\Shared\navMenu.cshtml:8
System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +279
System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +125
In summary, I was hoping the ContentFinder would let me fake some content for my RenderMvcControllers but it looks like the IsReservedPathOrUrl check stops the UmbracoModule from ever calling the finders, so that won't work. I'm sure what I'm trying to do is an edge case, but if this seems like something that should work, I'll be glad to be the guinea pig for fixes.
OK, looking at the code, UmbracoModule considers that if you've got a custom route for the url, then it's not an Umbraco request, then there's no need to initialize Umbraco to render some content. And then it's unsafe to render an UmbracoTemplatePage. If you want to render an UmbracoTemplatePage, do NOT configure the route.
Then, we will assume it's a content request. And the finders will run. At that point you want to register a finder that detects the url, picks a content node (probably the root node, as you already figured out) and more important, picks a template. Which would be your partial. No idea if it works with a partial, nor whether it needs to be in /views or can be in /views/shared.
But at least, you'll have a context and everything.
Interesting idea. I'll try to run everything in the finder instead. Feels more natural to run it via an action in a RenderMvcController, but I know I'm doing something pretty unorthodox.
Well, Umbraco 6 is not a "true" MVC app, since we have to run the legacy webforms. So we do generic stuff before handling control over to either legacy webforms, or MVC. It would be OK to run it via an action in a controller (it's not so unorthodox) if we were a "true" MVC app, but at the moment when you do so, you bypass all the generic stuff -- that is required for everything to run smoothly.
Thank you Randy and Stephen, this post is helpful to finally come across after a two-week-long struggle of trying to render a template from within my own "true" MVC app and totally failing this whole time. I need my custom routing, and would like to be able to render umbraco views, so as unfortunate as this is, I'm glad that I can stop trying now and realize it simply wasn't ever destined to work. On the bright side, I have a better idea now of what to expect and what can work vs. not-work.
P.S., what is a ContentFinder? Nothing in the documentation about this and the only info I can find is maybe U4-1327; are there any samples anywhere (or planned perhaps) showing how one might be able to use this for any custom purpose?
What a great document, Stéphane, thank you! Makes me want to fly halfway around the globe for CG14! (and hope for videos of CG13.)
Not sure if this is the best place to ask questions about your slides? Please let me know if otherwise.
On page 45, regarding the PublishedContentRequest, it says: "All this happens when the Umbraco modules thinks it's a document to render." Question: is there a way to manually build or load one of these PCR's when we are using custom routing? (Side-stepping the built-in Umbraco routing?) Example: my custom route kicks in via some complicated & deep URL. I want to render a view and pretend I'm the homepage. How? (Or should I just stop hoping for this and refer to page 63?)
Trouble rendering partial views from RenderMvcController
Note: This is all against 6.1 tip as of Apr10. Moving conversation here per Stephan's request.
I'm trying to do something a little unorthodox and running into some issues. I'm trying to render some of my partial views (nav, footer) to an external endpoint so they can be shared with some WordPress blogs. I created a simple RenderMvcController to handle this.
public class ExternalContentController : mvc.RenderMvcController
{
public ActionResult Content(string viewName)
{
return PartialView(viewName);
}
}
With only this in place a request to say, /external/navMenu, will throw an exception inside the view if it references UmbracoContext because there is no PublishedContentRequest. I tried to fix this with a ContentFinder that would just set the the PCR to the site homepage if the controller name was "ExternalContent", but then I found that the finders are never even called because the route fails the EnsureUmbracoRoutablePage check in UmbracoModule.ProcessRequest.
I'm guessing the traditional (4.x, but assuming MVC was there;)) Umbraco way to do this would have been to have expose the partials in a content node with the partial as its template, but that feels pretty dirty to me. I also think I might run into issues later if the partials have to be in /views and can't be under /views/shared.
Any ideas?
How do you plumb everything so that ExternalContentController runs? Do you register routes? Have you declared "external" as a reserved path?
When you say it fails the EnsureUmbracoRoutablePage... how do you know? Have you stepped through the code? Do you have an UmbracoRoutableOutcome reason that would explain why it failed?
When you say it throws an exception... can you post the exact stack trace here?
Yes, the routes are wired up in my application, which is why my action gets called. Stepping thru a little bit more, it looks like GlobalSettings.IsReservedPathOrUrl returns true as expected, which causes EnsureDocumentRequest to be false.
If the partial in question is HTML-only, everything works just fine. The exception comes when referencing the UmbracoContext or Helper in the partial. If I reference the Umbraco helper first, I get ...
In summary, I was hoping the ContentFinder would let me fake some content for my RenderMvcControllers but it looks like the IsReservedPathOrUrl check stops the UmbracoModule from ever calling the finders, so that won't work. I'm sure what I'm trying to do is an edge case, but if this seems like something that should work, I'll be glad to be the guinea pig for fixes.
OK, looking at the code, UmbracoModule considers that if you've got a custom route for the url, then it's not an Umbraco request, then there's no need to initialize Umbraco to render some content. And then it's unsafe to render an UmbracoTemplatePage. If you want to render an UmbracoTemplatePage, do NOT configure the route.
Then, we will assume it's a content request. And the finders will run. At that point you want to register a finder that detects the url, picks a content node (probably the root node, as you already figured out) and more important, picks a template. Which would be your partial. No idea if it works with a partial, nor whether it needs to be in /views or can be in /views/shared.
But at least, you'll have a context and everything.
Interesting idea. I'll try to run everything in the finder instead. Feels more natural to run it via an action in a RenderMvcController, but I know I'm doing something pretty unorthodox.
Well, Umbraco 6 is not a "true" MVC app, since we have to run the legacy webforms. So we do generic stuff before handling control over to either legacy webforms, or MVC. It would be OK to run it via an action in a controller (it's not so unorthodox) if we were a "true" MVC app, but at the moment when you do so, you bypass all the generic stuff -- that is required for everything to run smoothly.
Let me know how it works!
Thank you Randy and Stephen, this post is helpful to finally come across after a two-week-long struggle of trying to render a template from within my own "true" MVC app and totally failing this whole time. I need my custom routing, and would like to be able to render umbraco views, so as unfortunate as this is, I'm glad that I can stop trying now and realize it simply wasn't ever destined to work. On the bright side, I have a better idea now of what to expect and what can work vs. not-work.
P.S., what is a ContentFinder? Nothing in the documentation about this and the only info I can find is maybe U4-1327; are there any samples anywhere (or planned perhaps) showing how one might be able to use this for any custom purpose?
Thank you!
Not much doc at the moment... you might want to look at that presentation from CG13: http://www.zpqrtbnk.net/TheUmbraco6RequestPipeline.pdf
What a great document, Stéphane, thank you! Makes me want to fly halfway around the globe for CG14! (and hope for videos of CG13.)
Not sure if this is the best place to ask questions about your slides? Please let me know if otherwise.
On page 45, regarding the PublishedContentRequest, it says: "All this happens when the Umbraco modules thinks it's a document to render."
Question: is there a way to manually build or load one of these PCR's when we are using custom routing? (Side-stepping the built-in Umbraco routing?)
Example: my custom route kicks in via some complicated & deep URL. I want to render a view and pretend I'm the homepage. How? (Or should I just stop hoping for this and refer to page 63?)
Thanks again!
is working on a reply...