Try our conversational search powered by Generative AI!

How can I determine if contextAccessor.HttpContext.Request.Path corresponds to a partial route?


I'm attempting to generate a canonical link for a partial router. How can I determine if the Request.Path string URL belong to a partial router? Is there a default and straightforward method for achieving this?

Jan 06, 2024 20:01

Why do you need to compare them? You can always render the canonical URL.

Jan 07, 2024 13:20

@Html.CanonicalLink() or any other canonical link functional generates a URL from the content reference. Let's take an example of news partial routing example from  Developer documentation 

In the example, the part of the URL that is http://site/News/ is the URL to a page instance of model type NewsContainer. By registering a partial router for type NewsContainer, the partial router routes the remaining part of the URL. The partial router handles routing Sports/TheGame/.

public class NewsPartialRouter : IPartialRouter<NewsContainer, NewsContent>

If I am on the URL http://site/News/Sports/TheGame/. the @Html.CanonicalLink()  always output http://site/News/ instead of http://site/News/Sports/TheGame/

Edited, Jan 07, 2024 16:52

Hi Rich

We encountered the similar issue recently while using CanonicalLink html helper with Partial router. Our naive solution is to create a custom canonical url property and populated it's value when the current content's type is the specific type used in partial router, then render canonical url with this custom property in the layout instead of CanonicalLink. 


If (xxxPage is NewsContent) { customurl = "<get current request url from Request object>"}

In razor layout

@if(customurl has value) {// render canonical with customurl}  else{@Html.CanonicalLink()}

I hope above helps.

Edited, Jan 09, 2024 1:44

The URL Resolver should not cause a stack overflow. There must be an infinite loop in your partial router code that causes that. I just did a quick test with a partial router like this, and re-used the Standard Page view in the Alloy templates:

public class RoutedPartialObject
    public string Category { get; set; }

    public string Value { get; set; }

public class PartialRouter : IPartialRouter<StandardPage, RoutedPartialObject>
    // Store in an option and load during configuration.
    private static readonly ContentReference Root = new(11353);

    public PartialRouteData GetPartialVirtualPath(RoutedPartialObject content, UrlGeneratorContext urlGeneratorContext)
        return new PartialRouteData()
            BasePathRoot = Root,
            PartialVirtualPath = $"{content.Category}/{content.Value}"

    public object RoutePartial(StandardPage content, UrlResolverContext segmentContext)
        var counter = 0;
        var category = "";
        var value = "";

        // GetNextSegment should typically be called in a loop until
        // the segmentContext.RemainingSegments is empty, or everything
        // needed for this partial router has been extracted. Then update
        // if there is anything remaining and return the partial object.
        while (!segmentContext.RemainingSegments.IsEmpty)
            var segment = segmentContext.GetNextSegment();

            if (counter == 0)
                category = segment.Next.ToString();
            else if (counter == 1)
                value = segment.Next.ToString();
                // In this example we don't support more than two segments.
                segmentContext.RemainingSegments = null;

            // Update what we have consumed.
            segmentContext.RemainingSegments = segment.Remaining;


        return new RoutedPartialObject
            Category = category,
            Value = value,

public class RoutedPartialObjectController : Controller, IRenderTemplate<RoutedPartialObject>
    public ActionResult Index(StandardPage content)
        var model = new PageViewModel<StandardPage>(content);

        var routedObject = HttpContext.Features.Get<IContentRouteFeature>()
            .PartialRoutedObject as RoutedPartialObject;

        ViewData["RoutedPartialObject"] = routedObject;

        return View("~/Views/StandardPage/Index.cshtml", model);

Calling any method on the URL Resolver with this code does work.

The @Html.CanonicalLink() tag helper does not support partial routing unfortunately. As Vincent has suggested, you can render the link manually for the partially routed object instead. I will however add this as a feature request, because we should support this.

Edited, Jan 09, 2024 13:50

Hi Rich,

One approach to generating a canonical link for a partial router is to analyze the Request.Path string URL to determine if it belongs to a partial router. You can implement custom logic by parsing the URL and checking for distinctive markers that indicate a partial route.

As a helpful resource, you could look at the SEOBOOST library for Optimizely, which offers functionality for optimizing SEO-related aspects. Check out the GitHub repository here:

Feel free to explore the library's documentation and see if it aligns with your requirements.

Jan 19, 2024 11:46
Rich Hallman - Jan 19, 2024 17:20
thanks, Adnan I wasn't aware of this tool. If I had known about it earlier, it could have saved me a lot of headaches and time

Easiest is probably to check if the current HTTP Context has a partial routed object:

var routedObject = HttpContext.Features.Get<IContentRouteFeature>()
    .PartialRoutedObject as RoutedPartialObject;

Then generate the URL for this object:

var urlResolver = HttpContext.RequestServices.GetRequiredService<UrlResolver>();

var url = urlResolver.GetVirtualPathForNonContent(
        ForceCanonical = true,
        ForceAbsolute = true 

And finally compare this URL with the requested URL.

Jan 19, 2024 14:38
Rich Hallman - Jan 19, 2024 17:26
Johan, this is brilliant. I believe all I need to do is check whether routedObject is null or not
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.