Best practice on handling automatic 301 redirects

Hi,

I would like to get some pointers/best practice on how to handle old URL´s from a previous site. The plan is to leave the url on the product but change category names and main categories. I would like to create some kind of IPageNotFoundResolver that tries to find the product in the url and automatically redirect with an 301.

Litium version: 7.4

1 Like

This is my solution so far, any thoughts?

    public bool TryGet(RouteRequestLookupInfo routeRequestLookupInfo, out RouteRequestInfo routeRequestInfo)
        {
            var result = new RouteRequestInfo();
            if (_pageByFieldType.TryFindPage(page =>
           {

               List<BaseProduct> baseProducts;

               using (var query = _dataService.CreateQuery<BaseProduct>())
               {
                   var productOldUrl = routeRequestLookupInfo.Path.Split('/').LastOrDefault() ?? "";
                   baseProducts = query
                   .Filter(p => p.Field("_url", "eq", CultureInfo.GetCultureInfo("sv-SE"), productOldUrl)).ToList();
                   
                   if(baseProducts.Count > 0)
                   {
                       var productUrl = _urlService.GetUrl(baseProducts[0]);

                       if(productUrl != null)
                       {
                           result.PageSystemId = baseProducts[0].SystemId;
                           result.DataPath = productUrl;
                           result.RedirectPermanent = productUrl;
                           return true;
                       }
                   }
                   
               }

               var url = _urlService.GetUrl(page, new PageUrlArgs(routeRequestLookupInfo.Channel.SystemId));
               if (url == null)
               {
                   return false;
               }

               result.PageSystemId = page.SystemId;
               result.DataPath = url;
               return true;
           }))
            {
                routeRequestInfo = result;
                return true;
            }

            return _parentResolver.TryGet(routeRequestLookupInfo, out routeRequestInfo);
        }

Hi,
Your approach will cause severe performance issues since it will create a direct database query for each request. The simplest way is to export old urls and import as url redirects in BO/Settings/Websites/UrlRedirects.

Hi,

The problem with importing redirects is that we will be moving 1000 of products to new categories. It would take too long.

This is part of PageNotFoundResolverDecorator, wouldn’t it only trigger when no other page/product is found?

//Jonas

Before we go further, what is your previous site? If you are upgraded only changing the category url is not a problem since the url history mechanism will handle that one. I’m not sure about chnaging the main category, have to test it.

The previous site is not a Litium site.

You can still import all the products and categories with the old structure to Litium then change the category name, url in Litium. Then the url history will work.

I’ll be back with a suggestion for the IPageNotFoundResolver. It is better to use search instead of data query there.

Hi again,
Here comes a solution for your problem. You can use RoutingHelperService in your PageNotFoundResolver so that the caches are used and there will not be any performance issues.

using Litium.Accelerator.Caching;
using Litium.Application.Web.Routing;
using Litium.Products;
using Litium.Runtime.DependencyInjection;
using Litium.Web;
using Litium.Web.Routing;
using System;
using System.Globalization;
using System.Linq;

namespace Litium.Accelerator.Mvc.Routing
{
    [ServiceDecorator(typeof(IPageNotFoundResolver))]
    internal class PageNotFoundResolverDecorator : IPageNotFoundResolver
    {
        private readonly UrlService _urlService;
        private readonly PageByFieldTemplateCache<PageNotFoundByFieldTemplateCache> _pageByFieldType;
        private readonly RoutingHelperService _routingHelperService;
        private readonly BaseProductService _baseProductService;
        private readonly VariantService _variantService;
        private readonly IPageNotFoundResolver _parentResolver;

        public PageNotFoundResolverDecorator(
             IPageNotFoundResolver parentResolver,
             UrlService urlService,
             PageByFieldTemplateCache<PageNotFoundByFieldTemplateCache> pageByFieldType,
             RoutingHelperService routingHelperService, BaseProductService baseProductService, VariantService variantService)
        {
            _parentResolver = parentResolver;
            _urlService = urlService;
            _pageByFieldType = pageByFieldType;
            _routingHelperService = routingHelperService;
            _baseProductService = baseProductService;
            _variantService = variantService;
        }

        public bool TryGet(RouteRequestLookupInfo routeRequestLookupInfo, out RouteRequestInfo routeRequestInfo)
        {
            var result = new RouteRequestInfo();
            var url = routeRequestLookupInfo.Path.Split('/').LastOrDefault();
            var cultureInfo = CultureInfo.CurrentCulture;
            var channelSystemId = routeRequestLookupInfo.Channel.SystemId;
            BaseProduct baseProduct = null;
            var systemId = Guid.Empty;
            
            // Try to get the base product
            if (_routingHelperService.TryGetBaseProduct(url, cultureInfo, out systemId))
            {
                baseProduct = _baseProductService.Get(systemId);
            }

            if (baseProduct == null && _routingHelperService.TryGetVariant(url, cultureInfo, out systemId))
            {
                var variant = _variantService.Get(systemId);
                baseProduct = _baseProductService.Get(variant.BaseProductSystemId);
            }

            // Try to get the base product via url history
            if (baseProduct == null && _routingHelperService.TryGetProductHistoryUrl(cultureInfo, channelSystemId, url, out systemId, out bool isVariant))
            {
                if (isVariant)
                {
                    var variant = _variantService.Get(systemId);
                    baseProduct = _baseProductService.Get(variant.BaseProductSystemId);
                }
                else
                {
                    baseProduct = _baseProductService.Get(systemId);
                }
            }

            // Base product is found. Permanent redirect the correct url
            if (baseProduct != null)
            {
                result.RedirectPermanent = _urlService.GetUrl(baseProduct, new ProductUrlArgs(channelSystemId));
                routeRequestInfo = result;
                return true;
            }

            if (_pageByFieldType.TryFindPage(page =>
           {
               var url = _urlService.GetUrl(page, new PageUrlArgs(channelSystemId));
               if (url == null)
               {
                   return false;
               }

               result.PageSystemId = page.SystemId;
               result.DataPath = url;
               return true;
           }))
            {
                routeRequestInfo = result;
                return true;
            }

            return _parentResolver.TryGet(routeRequestLookupInfo, out routeRequestInfo);
        }
    }
}
1 Like

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.

Even that it is possible in Litium 7 to use Litium.Web.Routing.IPageNotFoundResolver to find and make the redirection the Litium.Web.Routing.IUrlRedirectResolver is more suitable for that purpose and need to be used from Litium 8.

1 Like