mass url redirect

Vote:
 

Hi

I've got about 300-400 URLs I need to redirect to their new location (some are unique and some are wildcard folders that redirect everything to a new single url). I've tried MS Microsoft URL rewrite redirection functionality but the EPiServer URL handler comes in before and then hits the 404 handler (outbound rules seem to work fine)

Is there any way around for this as I'd prefer not to have to create all these redirects on our traffic manager as that would produce more overall overhead because episerver is only replacing part of our site and we host/route alot of traffic to different servers for different apps via our traffic manager.

Thanks

Doug

#88140
Jul 02, 2014 17:53
Vote:
 

We are doing just that in one of our sites where we have two version of this, one that is automatically creating a redirect when someone is moving a page and one where admins can write their own.

both works with a simple table in a database with a from and to-address (the one with the automated created from page move also has the pageid so we can find the last location). To make it happen before any thing else we have created a section-routing that always happens before the render of page.

I can't access any code right now but will do tomorrow if you want.

#88144
Jul 02, 2014 22:34
Vote:
 

Hi Henrik

Any sample you have would be greatl received.

Thanks

Doug

#88168
Jul 03, 2014 14:43
Vote:
 

First, create a table to store the redirects (you can also store i as a xml-file, or in the dds but this is how we do it:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[PageRedirects](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[FromAddress] [varchar](500) NOT NULL,
	[ToAddress] [varchar](500) NOT NULL,
	[CreatedDate] [datetime] NOT NULL,
 CONSTRAINT [PK_PageRedirects] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[PageRedirects] ADD  CONSTRAINT [DF_PageRedirects_CreatedDate]  DEFAULT (getdate()) FOR [CreatedDate]
GO

Then create code to read and insert into this table:

First an entity

using System;

namespace CompanyName.Entities
{
    public class PageRedirect
    {
        public int Id { get; set; }
        public string FromAddress { get; set; }
        public string ToAddress { get; set; }
        public DateTime CreatedDate { get; set; }
    }
}

Then a interface for the database communication

using System.Collections.Generic;
using CompanyName.Entities;

namespace CompanyName.Persistance.Interfaces
{
    public interface IPageRedirectsRepository
    {
        PageRedirect GetMovePageByOldUrl(string oldurl);
        List GetAllPageRedirects();
        void AddPageRedirect(PageRedirect pageRedirect);
        void DeletePageRedirect(int id);
    }
}

Then the implementation of the interface, here we are using dapper as a microORM so you have to convert it to what you think is good, Databases.CompanyName.Execute is accually just doing the .Execute function of dapper.

using System.Collections.Generic;
using System.Linq;
using CompanyName.Entities;
using CompanyName.Persistance.Interfaces;

namespace CompanyName.ResourceAccess.Persistance
{
    public class PageRedirectsRepository: IPageRedirectsRepository
    {
        public PageRedirect GetMovePageByOldUrl(string oldurl)
        {
            const string queryString = @"SELECT top 1 ar.Id, ar.FromAddress, ar.ToAddress, ar.CreatedDate
                                        FROM PageRedirects ar
                                        WHERE FromAddress = @FromAddress OR (FromAddress + '/') = @FromAddress
                                        ORDER BY ar.CreatedDate desc";

            return Databases.CompanyName.Query(queryString, new { FromAddress = oldurl }).FirstOrDefault();
        }

        public List GetAllPageRedirects()
        {
            const string queryString = @"SELECT Id, FromAddress, ToAddress, CreatedDate
                                        FROM PageRedirects
                                        ORDER BY FromAddress";

            return Databases.CompanyName.Query(queryString).ToList();
        }

        public void AddPageRedirect(PageRedirect pageRedirect)
        {
            const string queryString = @"Insert Into PageRedirects
                                        (FromAddress, ToAddress)
                                        Values
                                        (@FromAddress, @ToAddress)";

            Databases.CompanyName.Execute(queryString, new { pageRedirect.FromAddress, pageRedirect.ToAddress });
        }

        public void DeletePageRedirect(int id)
        {
            const string queryString = @"Delete From PageRedirects Where Id = @Id";
            Databases.CompanyName.Execute(queryString, new { Id = id });
        }

    }
}

Now you have to see that the request use this also, and for that, create a section route like this: (there might be to many using or someone missing, I had to strip away a lot of other stuff)

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using EPiServer;
using EPiServer.Core;
using EPiServer.Editor;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
using EPiServer.Web.Routing;
using EPiServer.Web.Routing.Segments;
using CompanyName.Helpers;

namespace CompanyName.Framework.Initialization
{
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class RoutesInitialization : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            var redirectSegment = new RedirectSegment("redirect");
            var routingParameters = new MapContentRouteParameters()
            {
                SegmentMappings = new Dictionary()
            };
            routingParameters.SegmentMappings.Add("redirect", redirectSegment);
            
			RouteTable.Routes.MapContentRoute(
              name: "redirectterms",
              url: "{language}/{node}/{redirect}",
              defaults: new { action = "index" },
              parameters: routingParameters);

        }

        public void Uninitialize(InitializationEngine context)
        {
        }

        public void Preload(string[] parameters)
        {
        }
    }

    public class RedirectSegment : SegmentBase
    {
        public RedirectSegment(string name)
            : base(name)
        {
        }

        public override bool RouteDataMatch(SegmentContext context)
        {
            if (PageEditing.PageIsInEditMode)
                return false;

            string redirectUrl;
            if (HttpContext.Current.Request.Is301(out redirectUrl))
            {
                context.PermanentRedirect(redirectUrl);
                context.RemainingPath = string.Empty;
                return true;
            }
            return false;
        }

        public override string GetVirtualPathSegment(RequestContext requestContext, RouteValueDictionary values)
        {
            return null;
        }
    }

}

The HttpContext.Current.Request.Is301 is a helper method that looks like this:

using System.Web;
using EPiServer;

namespace CompanyName.Framework.Extensions
{
    public static class RequestExtensions
    {
        public static bool Is301(this HttpRequest request, out string redirectUrl)
        {
            string requestedUrl = request.Url.AbsolutePath;

			var pageRedirectsRepository = new PageRedirectsRepository();

            requestedUrl = requestedUrl.ToLower();

            var newUrl = pageRedirectsRepository.GetMovePageByOldUrl(requestedUrl);

            redirectUrl = newUrl != null && newUrl.ToAddress.ToLowerInvariant() != requestedUrl ? newUrl.ToAddress : "";

            return !String.IsNullOrWhiteSpace(redirectUrl);
			
            if (!String.IsNullOrWhiteSpace(redirectUrl))
            {
                return true;
            }

            return false;
        }
	}
}

What you have to watch out for is that this is going to run for every request so if you do not have indexed the database good and suffer performance-problem you might want to add some cache'ing.

I have ripped this code out of its context and changes some namespacename to remove the customers name so you can not take it straight up and run it, you have to modified it a little to work in your code.

As a litte bonus, to add a admin-page to manage these redirect, just add a page like this:

<%@ page language="C#" autoeventwireup="False" codebehind="Edit301Urls.aspx.cs" inherits="CompanyName.Plugins.Admin.Edit301Urls" %>

    
Skapa ny ompekning
Radera ompekning
Befintliga url'er <% if (existingredirects !="null)" { foreach (var url in existingredirects) {%> <%}%> <%} %>
Id Från Till Skapad
<%=url.id %> <%=url.fromaddress %> <%=url.toaddress %> <%=url.createddate.tostring("yymmdd") %>

Whit this codebehind:

using System;
using System.Collections.Generic;
using CompanyName.Entities;
using CompanyNameCompanyName.Persistance;
using EPiServer.PlugIn;

namespace CompanyName.Plugins.Admin
{
    [GuiPlugIn(DisplayName = "Redigera 301-sidor", Description = "Sida för att hantera 301-sidor", Area = PlugInArea.AdminMenu, Url = "~/Plugins/Admin/Edit301Urls.aspx")]
    public partial class Edit301Urls : EPiServer.UI.SystemPageBase
    {
        private PageRedirectsRepository _pageRedirectsRepository;

        protected override void OnPreInit(EventArgs e)
        {
            base.OnPreInit(e);
            MasterPageFile = ResolveUrlFromUI("MasterPages/EPiServerUI.master");
            SystemMessageContainer.Heading = "Skapa/Redigera ompekningar";
            SystemMessageContainer.Description = "Skapa ny ompekning genom att fylla i från och till-adresser";
            _pageRedirectsRepository = new PageRedirectsRepository();
            ExistingRedirects = _pageRedirectsRepository.GetAllPageRedirects();
        }

        public List ExistingRedirects { get; set; }

       
        protected void UpdateSubmitButtonToView_OnClick(object sender, EventArgs e)
        {
            var pageRedirect = new PageRedirect {FromAddress = FromAddress.Text, ToAddress = ToAddress.Text};
            _pageRedirectsRepository.AddPageRedirect(pageRedirect);
            ExistingRedirects = _pageRedirectsRepository.GetAllPageRedirects();

            FromAddress.Text = "";
            ToAddress.Text = "";
        }

        protected void DeleteButton_OnClick(object sender, EventArgs e)
        {
            int id = 0;
            if (int.TryParse(RedirectId.Text, out id))
            {
                _pageRedirectsRepository.DeletePageRedirect(id);
                RedirectId.Text = "";
                ExistingRedirects = _pageRedirectsRepository.GetAllPageRedirects();
            }
        }
    }
}

Yes, I know, there is a little swedish in it and it is ugly webform, but it works, and I have not had the time to convert these things into MVC.

Hope this helps!

#88176
Jul 03, 2014 22:10
Vote:
 

Many thanks for sharing :)

#88203
Jul 04, 2014 15:24
Vote:
 

Hope it helps you.

Remember to mark it as an answear if you think it answear your question.

One thing, If you like it to work with star, you have to modifiy the sql-question for getting the url.

#88214
Jul 04, 2014 18:50
Vote:
 

Hi

Are you sure your rules are correctly set up for IIS Url Rewrite (that they are actually matched)?

For example I have the Alloy site and I added two new pages which addresses are demo and demo2 (urls to root relative: /demo and /demo2).

Rule to just redirect one page namely /demo to /demo2


	
	

So notice that the match url doesn't contain the leading '/'

As you said you have a bunch of urls to redirect then you most likely want to use the rewriteMaps like this:


	
	
		
	
	


	
		
	

NOTICE! When using the rewritemap you have the leading '/' as it comes from the REQUEST_URI parameter.

Use failed request tracing to see are your rules working or not: http://www.iis.net/learn/extensions/url-rewrite-module/using-failed-request-tracing-to-trace-rewrite-rules

See reqritemaps reference: http://www.iis.net/learn/extensions/url-rewrite-module/using-rewrite-maps-in-url-rewrite-module

Maybe the above helps you use the IIS Url Rewrite module to accomplish your redirects.

Cheers.

#88215
Jul 05, 2014 13:25
Vote:
 

Thank Henrik Fransas, I don't know that we have a method to set redirection here. Save my day!

#112255
Oct 27, 2014 8:09
Vote:
 

Great to know Dzung! Glad to be able to help.

#112257
Oct 27, 2014 8:43
Vote:
 

We have exaclty similar code for custom SegmentBase and Routing in commerce 12 and now we are upgrading to commercer 14. We noticed SegmentBase and ISegment not exists in Commercer 14, any idea how to replace custom segement base and routing intialization?

#314453
Dec 20, 2023 13:24
* 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.