Hello,
Our client wanted custom endpoints for products and categories, so I used this topic as a solution to this request. It seemed to work just fine until I wanted to access a subcategory, which resulted in a Page not found
error.
The implementation works fine for products and main categories but shows the wrong URL for subcategories. As a result, no products are shown on the page.
Here are some examples:
Our client wanted to have the prefix produkt
on every product. This seems to work fine.
Same applies for main categories. The custom URL works fine here as well. The main category in this case is
Spillskydd
and we can see that the endpoint is /productkategori/spillskydd/
The problem occurs when I attempt to access a subcategory. If I want to navigate to absorbenter
, for instance, I get redirected to the following page:
Notice the URL. It should be /productkategori/spillskydd/absorbenter/
, but it is instead /productkategori/absorbenter/
. I assume this is the reason why the page cannot be found.
I have tried modifying UrlServiceDecorator
and RoutingServiceDecorator
further, but without any success.
Here are the contents of the two files:
RoutingServiceDecorator
using System;
using System.Linq;
using JetBrains.Annotations;
using Litium.Application.Web.Routing;
using Litium.FieldFramework;
using Litium.Products;
using Litium.Runtime.DependencyInjection;
namespace Litium.Accelerator.Mvc.Routing
{
[ServiceDecorator(typeof(RoutingService))]
public class RoutingServiceDecorator : RoutingService
{
private readonly RoutingService _routingService;
private readonly RoutingHelperService _routingHelperService;
private readonly BaseProductService _baseProductService;
private readonly VariantService _variantService;
private readonly FieldTemplateService _fieldTemplateService;
private readonly DisplayTemplateService _displayTemplateService;
private readonly CategoryService _categoryService;
public RoutingServiceDecorator(RoutingService routingService,
RoutingHelperService routingHelperService,
BaseProductService baseProductService,
VariantService variantService,
FieldTemplateService fieldTemplateService,
DisplayTemplateService displayTemplateService,
CategoryService categoryService)
{
_routingService = routingService;
_routingHelperService = routingHelperService;
_baseProductService = baseProductService;
_variantService = variantService;
_fieldTemplateService = fieldTemplateService;
_displayTemplateService = displayTemplateService;
_categoryService = categoryService;
}
public override bool TryGet([NotNull] AssortmentRouteLookupInfo lookupInfo, out RouteInfo routeInfo)
{
var result = _routingService.TryGet(lookupInfo, out routeInfo);
Guid systemId = Guid.Empty;
BaseProduct baseProduct = null;
Variant variant = null;
Category category = null;
var websiteSystemId = lookupInfo.WebsiteSystemId;
var lookupSegments = lookupInfo.Segments;
if (_routingHelperService.TryGetBaseProduct(lookupSegments[^1], lookupInfo.CultureInfo, out systemId))
{
baseProduct = _baseProductService.Get(systemId);
}
else if (_routingHelperService.TryGetVariant(lookupSegments[^1], lookupInfo.CultureInfo, out systemId))
{
variant = _variantService.Get(systemId);
baseProduct = _baseProductService.Get(variant.BaseProductSystemId);
}
else if (_routingHelperService.TryGetCategory(Guid.Empty, lookupSegments[^1], lookupInfo.CultureInfo, out Guid categorySystemId, lookupInfo.AssortmentSystemId))
{
category = _categoryService.Get(categorySystemId);
}
if (category != null)
{
var categoryFieldTemplate = _fieldTemplateService.Get<CategoryFieldTemplate>(category.FieldTemplateSystemId);
var categoryDisplayTemplate = _displayTemplateService.Get<CategoryDisplayTemplate>(categoryFieldTemplate.DisplayTemplateSystemId);
var categoryTemplatePath = categoryDisplayTemplate.Templates.FirstOrDefault(x => x.WebSiteSystemId == websiteSystemId)?.Path
?? categoryDisplayTemplate?.TemplatePath;
routeInfo = new CategoryRouteInfo(categoryTemplatePath, categoryDisplayTemplate?.Id, category)
{
Category = category
};
return !string.IsNullOrEmpty(categoryTemplatePath) || result;
}
if (baseProduct != null)
{
var fieldTemplate = _fieldTemplateService.Get<ProductFieldTemplate>(baseProduct.FieldTemplateSystemId);
var displayTemplate = _displayTemplateService.Get<ProductDisplayTemplate>(fieldTemplate.DisplayTemplateSystemId);
var mainCategory = baseProduct.GetMainCategory(lookupInfo.ChannelSystemId);
var templatePath = string.Empty;
if (mainCategory != null)
{
templatePath = displayTemplate.Templates.FirstOrDefault(x => x.WebSiteSystemId == websiteSystemId)?.Path ?? displayTemplate?.TemplatePath;
}
if (!string.IsNullOrEmpty(templatePath))
{
routeInfo = new ProductRouteInfo(templatePath, displayTemplate?.Id, baseProduct, mainCategory)
{
Variant = variant
};
result = true;
}
}
return result;
}
public override bool TryGet([NotNull] CategoryRouteLookupInfo lookupInfo, out RouteInfo routeInfo)
{
var result = _routingService.TryGet(lookupInfo, out routeInfo);
return result;
}
public override bool TryGet([NotNull] DomainNameRouteLookupInfo lookupInfo, out DomainNameRouteInfo routeInfo)
{
var result = _routingService.TryGet(lookupInfo, out routeInfo);
return result;
}
}
}
UrlServiceDecorator
using System;
using System.Linq;
using JetBrains.Annotations;
using Litium.Globalization;
using Litium.Products;
using Litium.Runtime;
using Litium.Runtime.DependencyInjection;
using Litium.Web;
using Litium.Web.Routing;
using Litium.Websites;
namespace Litium.Accelerator.Mvc.Routing
{
[ServiceDecorator(typeof(UrlService))]
public class UrlServiceDecorator : UrlService, IInfrastructure<RouteRequestLookupInfoAccessor>
{
private readonly UrlService _urlService;
public UrlServiceDecorator(UrlService urlService)
{
_urlService = urlService;
}
public RouteRequestLookupInfoAccessor Instance => ((IInfrastructure<RouteRequestLookupInfoAccessor>)_urlService).Instance;
public override string GetUrl([NotNull] BaseProduct baseProduct, [NotNull] ProductUrlArgs args)
{
return CustomUrlEndpoint(_urlService.GetUrl(baseProduct, args), args.AbsoluteUrl, "produkt"); // Temporarily hard-coded constant. TODO: Add multiculture fields so that both english and swedish values are available.
}
public override string GetUrl([NotNull] Variant variant, [NotNull] ProductUrlArgs args)
{
return CustomUrlEndpoint(_urlService.GetUrl(variant, args), args.AbsoluteUrl, "produkt"); // Temporarily hard-coded constant. TODO: Add multiculture fields so that both english and swedish values are available.
}
public override string GetUrl([NotNull] Category category, [NotNull] CategoryUrlArgs args)
{
return CustomUrlEndpoint(_urlService.GetUrl(category, args), args.AbsoluteUrl, "produktkategori"); // Temporarily hard-coded constant. TODO: Add multiculture fields so that both english and swedish values are available.
}
public override string GetUrl([NotNull] Page page, [NotNull] PageUrlArgs args)
{
return _urlService.GetUrl(page, args);
}
public override string GetUrl([NotNull] Channel channel, [NotNull] ChannelUrlArgs args)
{
return _urlService.GetUrl(channel, args);
}
public override string GetUrl([NotNull] DomainName domainName)
{
return _urlService.GetUrl(domainName);
}
private static string CustomUrlEndpoint(string originalUrl, bool absoluteUrl, string endpointPrefix)
{
if (string.IsNullOrWhiteSpace(originalUrl))
{
return originalUrl;
}
if (absoluteUrl)
{
var uri = new Uri(originalUrl);
return $"{uri.Scheme}://{uri.Host}/{uri.Segments.Last()}";
}
else
{
return $"/{endpointPrefix}/{originalUrl.Split('/').Last()}";
}
}
}
}
Litium version: 8.5.0