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.
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" %>
%@>
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!
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.
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.
Thank Henrik Fransas, I don't know that we have a method to set redirection here. Save my day!
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?
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