# Product base url

**URL:** <https://forum.litium.com/t/product-base-url/609>\
**Category:** Questions\
**Tags:** litium-7\
**Created:** [February 20, 2019, 1:06pm UTC](https://forum.litium.com/t/product-base-url/609 "2019-02-20T13:06:20Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![NiclasSparr](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/niclassparr/32/96_2.png) [@NiclasSparr](https://forum.litium.com/u/NiclasSparr)\
**Post date:** [February 20, 2019, 1:06pm UTC](https://forum.litium.com/t/product-base-url/609/1 "2019-02-20T13:06:20Z")

</div>

Is it possible to modify the product base url?  
Ex.  
[http://se.emnordic.spoton.se/cases/softcase/digitech-gb100](http://se.emnordic.spoton.se/cases/softcase/digitech-gb100)  
should be  
[http://se.emnordic.spoton.se/products/cases/softcase/digitech-gb100](http://se.emnordic.spoton.se/products/cases/softcase/digitech-gb100)

without adding a product category “products” and move all categories into that.

Litium version: 7.1.0

---

<div class="post-metadata">

**Author:** ![utku](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/utku/32/62_2.png) [@utku](https://forum.litium.com/u/utku)\
**Post date:** [February 20, 2019, 1:45pm UTC](https://forum.litium.com/t/product-base-url/609/2 "2019-02-20T13:45:04Z")

</div>

Hi,  
It is possible by decorating UrlService and RoutingService. I’ll try to write an article about that at the [docs.litium.com](http://docs.litium.com). I will put a link to the article under this question when I’m done.

---

<div class="post-metadata">

**Author:** ![NiclasSparr](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/niclassparr/32/96_2.png) [@NiclasSparr](https://forum.litium.com/u/NiclasSparr)\
**Post date:** [February 21, 2019, 7:34am UTC](https://forum.litium.com/t/product-base-url/609/3 "2019-02-21T07:34:54Z")

</div>

Hi, does this solution looks ok?  
(Except from putting productUrlPrefix in some better place)

```
using JetBrains.Annotations;
using Litium.Globalization;
using Litium.Products;
using Litium.Runtime;
using Litium.Web;
using Litium.Web.Routing;
using Litium.Websites;
using System;

namespace Litium.Accelerator.Mvc.Decorators
{
    [Litium.Runtime.DependencyInjection.ServiceDecorator(typeof(UrlService))]
    public class UrlServiceDecorator : UrlService, IInfrastructure<RouteRequestLookupInfoAccessor>
    {
        private readonly UrlService _parent;
        private string productUrlPrefix = "products";
        public UrlServiceDecorator(UrlService urlService)
        {
            _parent = urlService;
        }

        public RouteRequestLookupInfoAccessor Instance => ((IInfrastructure<RouteRequestLookupInfoAccessor>)_parent).Instance;

        public override string GetUrl([NotNull] BaseProduct baseProduct, [NotNull] ProductUrlArgs args)
        {
            var originalUrl = _parent.GetUrl(baseProduct, args);
            return ModifyUrl(originalUrl, args.AbsoluteUrl);
        }

        public override string GetUrl([NotNull] Variant variant, [NotNull] ProductUrlArgs args)
        {
            var originalUrl = _parent.GetUrl(variant, args);
            return ModifyUrl(originalUrl, args.AbsoluteUrl);
        }

        public override string GetUrl([NotNull] Category category, [NotNull] CategoryUrlArgs args)
        {
            var originalUrl = _parent.GetUrl(category, args);
            return ModifyUrl(originalUrl, args.AbsoluteUrl);
        }
        public override string GetUrl([NotNull] Page page, [NotNull] PageUrlArgs args) => _parent.GetUrl(page, args);
        public override string GetUrl([NotNull] Channel channel, [NotNull] ChannelUrlArgs args) => _parent.GetUrl(channel, args);

        private string ModifyUrl(string originalUrl, bool absoluteUrl)
        {
            if (string.IsNullOrWhiteSpace(originalUrl))
            {
                return originalUrl;
            }
            if (absoluteUrl)
            {
                var uri = new Uri(originalUrl);
                return uri.Scheme + "://" + uri.Host + "/" + productUrlPrefix + uri.AbsolutePath;
            }
            else
            {
                return "/" + productUrlPrefix + originalUrl;
            }
        }
    }
}

using JetBrains.Annotations;
using Litium.Application.Web.Routing;

namespace Litium.Accelerator.Mvc.Decorators
{
    [Litium.Runtime.DependencyInjection.ServiceDecorator(typeof(RoutingService))]
    public class RoutingServiceDecorator : RoutingService
    {
        private readonly RoutingService _parent;
        private string productUrlPrefix = "products";

        public RoutingServiceDecorator(RoutingService routingService)
        {
            _parent = routingService;
        }
        public override bool TryGet([NotNull] AssortmentRouteLookupInfo lookupInfo, out RouteInfo routeInfo)
        {
            if(lookupInfo.Segments.Length > 1 && lookupInfo.Segments[0] == productUrlPrefix)
            {
                lookupInfo = new AssortmentRouteLookupInfo(
                    lookupInfo.CultureInfo, 
                    lookupInfo.Segments.Slice(1), 
                    lookupInfo.WebsiteSystemId, 
                    lookupInfo.AssortmentSystemId,
                    lookupInfo.ChannelSystemId,
                    lookupInfo.UseHistory, 
                    lookupInfo.DisplayOnWebsiteSystemId);
            }
            var result = _parent.TryGet(lookupInfo, out routeInfo);
            return result;
        }
        public override bool TryGet([NotNull] CategoryRouteLookupInfo lookupInfo, out RouteInfo routeInfo)
        {
            if (lookupInfo.Segments.Length > 1 && lookupInfo.Segments[0] == productUrlPrefix)
            {
                lookupInfo = new CategoryRouteLookupInfo(
                    lookupInfo.CultureInfo,
                    lookupInfo.Segments.Slice(1),
                    lookupInfo.WebsiteSystemId,
                    lookupInfo.CategorySystemId, 
                    lookupInfo.ChannelSystemId,
                    lookupInfo.UseHistory);
            }
            var result = _parent.TryGet(lookupInfo, out routeInfo);
            return result;
        }

        public override bool TryGet([NotNull] DomainNameRouteLookupInfo lookupInfo, out DomainNameRouteInfo routeInfo)
        {
            var result = _parent.TryGet(lookupInfo, out routeInfo);
            return result;
        }
    }
}
```

---

<div class="post-metadata">

**Author:** ![utku](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/utku/32/62_2.png) [@utku](https://forum.litium.com/u/utku)\
**Post date:** [February 21, 2019, 8:15am UTC](https://forum.litium.com/t/product-base-url/609/4 "2019-02-21T08:15:57Z")

</div>

Great! I don’t need to write an article about it now. You can maybe save the prouductUrlPrefix as a field on the channel. You have always access to the channelId in both UrlService and RoutingService.

---

<div class="post-metadata">

**Author:** ![steve.redstorm](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/steve.redstorm/32/14_2.png) [@steve.redstorm](https://forum.litium.com/u/steve.redstorm)\
**Post date:** [February 21, 2019, 8:24am UTC](https://forum.litium.com/t/product-base-url/609/5 "2019-02-21T08:24:14Z")

</div>

> [@NiclasSparr](#):
>
> return uri.Scheme + “://” + uri.Host + “/” + productUrlPrefix + uri.AbsolutePath;

Those parts/lines with “+” would look better with $“” instead (as suggestions) 🙂

---

<div class="post-metadata">

**Author:** ![Ericsj11](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/ericsj11/32/899_2.png) [@Ericsj11](https://forum.litium.com/u/Ericsj11)\
**Post date:** [May 26, 2020, 11:39am UTC](https://forum.litium.com/t/product-base-url/609/6 "2020-05-26T11:39:48Z")

</div>

@utku Do you see any way this could be used to have products url completely without category url?

For example:  
[http://se.emnordic.spoton.se/products/digitech-gb100](http://se.emnordic.spoton.se/products/digitech-gb100)  
Or even better  
[http://se.emnordic.spoton.se/digitech-gb100](http://se.emnordic.spoton.se/digitech-gb100)

Use case:  
1 Product in many categories, where category urls will look confusing for the user

---

<div class="post-metadata">

**Author:** ![NilsN](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/nilsn/32/302_2.png) [@NilsN](https://forum.litium.com/u/NilsN)\
**Post date:** [May 26, 2020, 1:56pm UTC](https://forum.litium.com/t/product-base-url/609/7 "2020-05-26T13:56:33Z")

</div>

Maybe you could use something like this (modified version of above):

**UrlServiceDecorator**

```auto
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;
using System;
using System.Linq;

namespace Litium.Accelerator.Mvc.Decorators
{
	[ServiceDecorator(typeof(UrlService))]
	public class UrlServiceDecorator : UrlService, IInfrastructure<RouteRequestLookupInfoAccessor>
	{
		private readonly UrlService _parent;
		public UrlServiceDecorator(UrlService urlService)
		{
			_parent = urlService;
		}

		public RouteRequestLookupInfoAccessor Instance => ((IInfrastructure<RouteRequestLookupInfoAccessor>)_parent).Instance;

		public override string GetUrl([NotNull] BaseProduct baseProduct, [NotNull] ProductUrlArgs args)
		{
			var originalUrl = _parent.GetUrl(baseProduct, args);
			return ModifyUrl(originalUrl, args.AbsoluteUrl);
		}

		public override string GetUrl([NotNull] Variant variant, [NotNull] ProductUrlArgs args)
		{
			var originalUrl = _parent.GetUrl(variant, args);
			return ModifyUrl(originalUrl, args.AbsoluteUrl);
		}

		public override string GetUrl([NotNull] Category category, [NotNull] CategoryUrlArgs args) => _parent.GetUrl(category, args);
		public override string GetUrl([NotNull] Page page, [NotNull] PageUrlArgs args) => _parent.GetUrl(page, args);
		public override string GetUrl([NotNull] Channel channel, [NotNull] ChannelUrlArgs args) => _parent.GetUrl(channel, args);

		private string ModifyUrl(string originalUrl, bool absoluteUrl)
		{
			if (string.IsNullOrWhiteSpace(originalUrl))
			{
				return originalUrl;
			}

			if (absoluteUrl)
			{
				var uri = new Uri(originalUrl);
				return $"{uri.Scheme}://{uri.Host}/{uri.Segments.Last()}";
			}
			else
			{
				return $"/{originalUrl.Split('/').Last()}";
			}
		}
	}
}

```

**RoutingServiceDecorator**

```auto
using JetBrains.Annotations;
using Litium.Application.Web.Routing;
using Litium.FieldFramework;
using Litium.Foundation.Modules.ExtensionMethods;
using Litium.Products;
using Litium.Runtime.DependencyInjection;
using System;
using System.Linq;

namespace Litium.Accelerator.Mvc.Decorators
{
	[ServiceDecorator(typeof(RoutingService))]
	public class RoutingServiceDecorator : RoutingService
	{
		private readonly RoutingService _parent;
		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)
		{
			_parent = 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 = _parent.TryGet(lookupInfo, out routeInfo);

			if (!result)
			{
				Variant variant = null;
				BaseProduct baseProduct = null;

				if (_routingHelperService.TryGetBaseProduct(lookupInfo.Segments[0], lookupInfo.CultureInfo, out Guid systemId))
				{
					baseProduct = _baseProductService.Get(systemId);
				}
				else if (_routingHelperService.TryGetVariant(lookupInfo.Segments[0], lookupInfo.CultureInfo, out systemId))
				{
					variant = _variantService.Get(systemId);
					baseProduct = _baseProductService.Get(variant.BaseProductSystemId);
				}

				if (baseProduct != null)
				{
					var category = baseProduct.GetMainCategory(lookupInfo.ChannelSystemId);

					if (category != null)
					{
						var websiteSystemId = lookupInfo.WebsiteSystemId;
						var fieldTemplate = _fieldTemplateService.Get<ProductFieldTemplate>(baseProduct.FieldTemplateSystemId);
						var displayTemplate = _displayTemplateService.Get<ProductDisplayTemplate>(fieldTemplate.DisplayTemplateSystemId);
						var templatePath = displayTemplate.Templates.FirstOrDefault(x => x.WebSiteSystemId == websiteSystemId)?.Path ?? displayTemplate.TemplatePath;
						if (templatePath != null)
						{
							routeInfo = new ProductRouteInfo(templatePath, baseProduct, category) { Variant = variant };
							return true;
						}
					}
				}
			}

			return result;
		}

		public override bool TryGet([NotNull] CategoryRouteLookupInfo lookupInfo, out RouteInfo routeInfo) => _parent.TryGet(lookupInfo, out routeInfo);

		public override bool TryGet([NotNull] DomainNameRouteLookupInfo lookupInfo, out DomainNameRouteInfo routeInfo) => _parent.TryGet(lookupInfo, out routeInfo);
	}
}

```

---

<div class="post-metadata">

**Author:** ![Ericsj11](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/ericsj11/32/899_2.png) [@Ericsj11](https://forum.litium.com/u/Ericsj11)\
**Post date:** [May 27, 2020, 6:57am UTC](https://forum.litium.com/t/product-base-url/609/8 "2020-05-27T06:57:48Z")

</div>

Looks like it working great! This should change the urls for product feeds aswell right?  
Thanks Nils!

---

<div class="post-metadata">

**Author:** ![NilsN](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/nilsn/32/302_2.png) [@NilsN](https://forum.litium.com/u/NilsN)\
**Post date:** [May 27, 2020, 7:27am UTC](https://forum.litium.com/t/product-base-url/609/9 "2020-05-27T07:27:45Z")

</div>

I didn’t test that, but yes, it should, since that one calls the UrlService as well.

---

<div class="post-metadata">

**Author:** ![felixfroberg](https://avatars.discourse-cdn.com/v4/letter/f/4bbf92/32.png) [@felixfroberg](https://forum.litium.com/u/felixfroberg)\
**Post date:** [February 8, 2022, 5:06pm UTC](https://forum.litium.com/t/product-base-url/609/10 "2022-02-08T17:06:36Z")

</div>

I’ve got the same request from a customer, and tried the above solution but only the first part of it works. UrlServiceDecorator works as it should and gives me new URLs for each product, but it looks like RoutingService was removed in later versions of Litium (this customer is running 7.7.1). My guess is that I would need to decorate PageRouteResolver instead, is that correct? If so, I’d gladly accept any pointers in the right direction to help me get started with this!

Regards,  
Felix

---

<div class="post-metadata">

**Author:** ![NilsN](https://yyz2.discourse-cdn.com/flex030/user_avatar/forum.litium.com/nilsn/32/302_2.png) [@NilsN](https://forum.litium.com/u/NilsN)\
**Post date:** [February 9, 2022, 8:41am UTC](https://forum.litium.com/t/product-base-url/609/11 "2022-02-09T08:41:54Z")

</div>

It should work with 7.7.1 as well, make sure the Litium.Application package (that has RoutingService) is added to the project.

---

<div class="post-metadata">

**Author:** ![felixfroberg](https://avatars.discourse-cdn.com/v4/letter/f/4bbf92/32.png) [@felixfroberg](https://forum.litium.com/u/felixfroberg)\
**Post date:** [February 23, 2022, 5:58pm UTC](https://forum.litium.com/t/product-base-url/609/12 "2022-02-23T17:58:29Z")

</div>

I finally got around to test this, and it seems to be working. Not sure what the issue was the first time around but this time it is working just fine. Thanks!
