Search code examples
c#asp.net-corekenticokentico-kontent

How to resolve content item links in Kentico Cloud while respecting routes configuration?


ASP.NET Core 2.2 web site shows data from a Kentico Cloud CMS. Some of the items contains a rich text field. These fields might contain links to another content items. These links should be resolved to an url by the web site.

This docs suggest that this is done by implementing an IContentLinkUrlResolver interface. However, the sample is too dummy for the purpose of our site. Our resolver needs to know context of the request (like the current UI culture as the site is multi-locale) and route definitions as this is the only place which defines how the urls should look like.

In fact, the resolver needs to be able to call Url.Action as it would be able to inside a view.

The url resolver should respect the routes and the current ui culture. Its logic is expected to be like:

if (the linked content item type is Page)
{
    Url.Action("Page", "Home", new [] { codename = content item’s codename });
}
else if (the linked content item type is PageFont)
{
    Url.Action("Font", "Home", new [] { codename = content item’s codename });
}
else
{
    throw an error about an unsupported content type.
}

The rules are defined as:

var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();

app.UseRouter(routes =>
{
    routes.MapMiddlewareRoute("{culture=en-US}/{*mvcRoute}", subApp =>
    {
        subApp.UseRequestLocalization(localizationOptions.Value);

        subApp.UseMvc(mvcRoutes =>
        {
            mvcRoutes.MapRoute(
                name: "areas",
                template: "{culture=en-US}/{area:exists}/{controller=Home}/{action=Index}/{id?}");

            mvcRoutes.MapRoute(
                name: "sitemap",
                defaults: new { controller = "Sitemap", action = "Index" },
                template: "sitemap.xml");

            mvcRoutes.MapRoute(
                name: "font",
                defaults: new { controller = "Home", action = "Font" },
                template: "{culture=en-US}/font/{codename}");

            mvcRoutes.MapRoute(
                name: "page",
                defaults: new { controller = "Home", action = "Page" },
                template: "{culture=en-US}/{codename}");

            mvcRoutes.MapRoute(
                name: "default",
                template: "{culture=en-US}/{controller=Home}/{action=Index}/{id?}");
        });
    });
});

How to resolve links respecting the routes in the site configuration?


Solution

  • This should be able to resolve URLs similar to Url.Action in a view:

    In Startup.cs:

    services
        .TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
    
    services
        .AddSingleton<IContentLinkUrlResolver, RoutesContentLinkUrlResolver>()
        .AddDeliveryClient( ... )
    ;
    

    In RoutesContentLinkUrlResolver.cs:

    private readonly IUrlHelperFactory urlHelperFactory;
    private readonly IActionContextAccessor actionContextAccessor;
    
    public RoutesContentLinkUrlResolver(IUrlHelperFactory urlHelperFactory, IActionContextAccessor actionContextAccessor)
    {
        this.urlHelperFactory = urlHelperFactory;
        this.actionContextAccessor = actionContextAccessor;
    }
    
    public string ResolveLinkUrl(ContentLink link)
    {
        IUrlHelper Url = urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext);
    
        switch (link.ContentTypeCodename)
        {
            case "Page":
                return Url.Action("Page", "Home", new { codename = link.Codename });
        }
    }
    

    The instance of IUrlHelper is retrieved as late as possible to make sure it has the latest runtime values. The default implementation of IUrlHelperFactory uses caching for performance: UrlHelperFactory line 44.