Umbraco custom mvc route doesn't set published content correctly
Hi,
I'm working on a wildcard page for Umbraco 13.0.3
The idea is the following: I have "connections" item in umbraco and and the url is /connections. Additionally, I have "details" item in umbraco under the "connections" item. So the default umbraco url is /connections/details. However, I want to have urls like this: /connections/123 where 123 is an id of the entity(it comes from different db).
After reading the docs I've come up with the following setup.
I have a composer to register a custom route:
public class RoutesComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.Services.Configure<UmbracoPipelineOptions>(
options =>
{
options.AddFilter(
new UmbracoPipelineFilter(nameof(ConnectionDetailsController))
{
Endpoints = app => app.UseEndpoints(
endpoints =>
{
endpoints.MapControllerRoute(
"Connection details controller",
"/connections/{id}",
new { Controller = "ConnectionDetails", Action = "Index" }
);
}
)
}
);
}
);
}
}
Here is my controller which inherits UmbracoPageController and IVirtualPageController
public class ConnectionDetailsController : UmbracoPageController, IVirtualPageController
{
public ConnectionDetailsController(
IUmbracoContextAccessor umbracoContextAccessor,
ILogger<UmbracoPageController> baseLogger,
ICompositeViewEngine compositeViewEngine
) : base(baseLogger, compositeViewEngine)
{
_umbracoContextAccessor = umbracoContextAccessor;
_logger = logger;
_publishedValueFallback = publishedValueFallback;
}
public IPublishedContent? FindContent(ActionExecutingContext actionExecutingContext)
{
if (_umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext))
{
var content = umbracoContext.Content.GetById(1387);
if (content != null) return content;
}
return null;
}
[HttpGet]
public IActionResult Index()
{
return View("AppPage", CurrentPage);
}
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly ILogger<ConnectionDetailsController> _logger;
private readonly IPublishedValueFallback _publishedValueFallback;
}
It works as a charm, umbraco sends the request to my controller, then FindContent resolves the correct PublishedContent item and on Index action I have proper item.
The interesting part comes when it comes to the View.
I have a several ViewComponents to render seo tags and other vanilla page parts like header and footer.
Here is an example:
[ViewComponent(Name = nameof(PageSeo))]
public class PageSeo : ViewComponent
{
public PageSeo(
IUmbracoContextAccessor context,
IConfiguration configuration,
IPublishedValueFallback publishedValueFallback
)
{
_context = context;
_umbracoContextFactory = umbracoContextFactory;
_configuration = configuration;
_publishedValueFallback = publishedValueFallback;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var viewModel = GetViewModel();
return await Task.FromResult((IViewComponentResult)View(viewModel));
}
private PageSeoViewModel GetViewModel()
{
var content = _context.GetRequiredUmbracoContext().PublishedRequest.PublishedContent; // PublishedContent is null
var seo = new Models.Generated.PageSeo(content, _publishedValueFallback);
var viewModel = new PageSeoViewModel();
viewModel.Title = seo.SeoTitle
viewModel.Description = seo.SeoDescription;
viewModel.Keywords = seo.SeoKeywords;
viewModel.Author = seo.SeoAuthor;
return viewModel;
}
private readonly IUmbracoContextAccessor _context;
private readonly IConfiguration _configuration;
private readonly IPublishedValueFallback _publishedValueFallback;
}
As you can see, it's working in the context of the current Umbraco page.
Here is a troublegiver:
var content = _context.GetRequiredUmbracoContext().PublishedRequest.PublishedContent; // PublishedContent is null
The content is always null.
Seems like the context hasn't been set correctly, and I'm not sure whether it's me doing something wrong or it's an umbraco bug.
There are a couple of workarounds in there (it looks like CreatePublishedRequest doesn't get called with the content found in your Find Content method)
But other than those workarounds, you can pass parameters into a View Component, so you could have...
public async Task<IViewComponentResult> InvokeAsync(IPublishedContent currentPage)
and then pass in the current page when the view component is invoked
Component.InvokeAsync("PageSeo", new { current Page = Model} )
Umbraco custom mvc route doesn't set published content correctly
Hi,
I'm working on a wildcard page for Umbraco 13.0.3 The idea is the following: I have "connections" item in umbraco and and the url is /connections. Additionally, I have "details" item in umbraco under the "connections" item. So the default umbraco url is /connections/details. However, I want to have urls like this: /connections/123 where 123 is an id of the entity(it comes from different db).
After reading the docs I've come up with the following setup.
I have a composer to register a custom route:
Here is my controller which inherits UmbracoPageController and IVirtualPageController
It works as a charm, umbraco sends the request to my controller, then FindContent resolves the correct PublishedContent item and on Index action I have proper item. The interesting part comes when it comes to the View.
I have a several ViewComponents to render seo tags and other vanilla page parts like header and footer. Here is an example:
And the component code:
As you can see, it's working in the context of the current Umbraco page. Here is a troublegiver:
The content is always null. Seems like the context hasn't been set correctly, and I'm not sure whether it's me doing something wrong or it's an umbraco bug.
Any ideas?
Thanks, Alexander.
Hi Alexander
You've done everything correctly... But this is an open bug on the tracker...
https://github.com/umbraco/Umbraco-CMS/issues/12834
There are a couple of workarounds in there (it looks like CreatePublishedRequest doesn't get called with the content found in your Find Content method)
But other than those workarounds, you can pass parameters into a View Component, so you could have...
public async Task<IViewComponentResult> InvokeAsync(IPublishedContent currentPage)
and then pass in the current page when the view component is invoked
Component.InvokeAsync("PageSeo", new { current Page = Model} )
Regards
Marc
is working on a reply...