Extending NewDefaultUrlProvider leads to error

I am following the main documentation here:

I want to implementing my own handling of urls that overrides the default one.

So I have done what the documentation shows:

using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Navigation;
using Umbraco.Cms.Core.Web;

namespace cms;

public class CustomUrlProvider : NewDefaultUrlProvider
{
  public CustomUrlProvider(
      IOptionsMonitor<RequestHandlerSettings> requestSettings,
      ILogger<NewDefaultUrlProvider> logger,
      ISiteDomainMapper siteDomainMapper,
      IUmbracoContextAccessor umbracoContextAccessor,
      UriUtility uriUtility,
      IPublishedContentCache publishedContentCache,
      IDomainCache domainCache,
      IIdKeyMap idKeyMap,
      IDocumentUrlService documentUrlService,
      IDocumentNavigationQueryService navigationQueryService,
      IPublishedContentStatusFilteringService publishedContentStatusFilteringService,
      ILanguageService languageService)
      : base(requestSettings,
        logger,
        siteDomainMapper,
        umbracoContextAccessor,
        uriUtility,
        publishedContentCache,
        domainCache,
        idKeyMap,
        documentUrlService,
        navigationQueryService,
        publishedContentStatusFilteringService,
      languageService)
  {
  }

  public override UrlInfo? GetUrl(IPublishedContent content, UrlMode mode, string? culture, Uri current)
  {
    // Get the default url from the base URL provider implementation (NewDefaultUrlProvider).
    UrlInfo? defaultUrlInfo = base.GetUrl(content, mode, culture, current);
    if (defaultUrlInfo?.Url is null)
    {
      // The base URL provider could not resolve a URL.
      return null;
    }

    var newUrl = $"{defaultUrlInfo.Url.ToString().TrimEnd('/')}/some-custom-path";
    return UrlInfo.FromUri(
        new Uri(newUrl, UriKind.RelativeOrAbsolute),
        Alias,
        defaultUrlInfo.Culture,
        defaultUrlInfo.IsExternal
    );
  }
}

My expectation here is to see that whatever page I save gets some-custom-path appended to it. Instead it fails, under “Links” in the Info panel I get:

This document is published but its URL cannot be routed

And while saving, this error pops up:

Which makes no sense really as there are no documents to collide with in my bare bones setup.

Any idea what’s wrong? Is the documentation missing something?
As soon as I start to return the exact url as calculated by the base implementation, such as:
var newUrl = $"{defaultUrlInfo.Url.ToString().TrimEnd('/')}"; then yes, it works fine again.

I had a go asking AI, and OPUS is confident in this assessment:

IUrlSegmentProvider → Generates URL segments that are stored in the database (umbracoDocumentUrl table)
IDocumentUrlService → Caches and retrieves those stored segments
IUrlProvider → Only for displaying URLs (reads FROM IDocumentUrlService, doesn’t write to it)

Which is not at all my takeaway from the documentation. Surely IUrlProvider is for calculating what URL something should be/have, no?

This is on Umbraco Cms 17.1.0.

Any tip on how to adjust the url like this?

More AI interpretations that I challenge, but it MIGHT be correct and actually explain why the documented approach isn’t working:

Key Takeaway
IUrlProvider = Display only (reads from IDocumentUrlService)
IUrlSegmentProvider = Persisted segments (writes to database)

The Umbraco documentation is confusing because IUrlProvider looks like the right place, but it’s only for transforming how URLs are displayed, not how they’re stored and routed.

@lawejo I saw your thread here:

Did you run into any similar issue when extending NewDefaultUrlProvider?
And did you find any solution to the caching issue?

I no longer have access to the code base in which I had this issue, but we circumvented the behavior by matching the GUID from URL with one I had available in the data I was processing. It’s kinda of a hack but I worked out. We did not attempt caching

1 Like