SharePoint URL redirection / rewriting using HttpModule for anonymous web application

Hello. Today I’ll write about how can we enable a redirection in the scope of SharePoint Site Collectionfrom something that does not exist but can be readable to something that does, but is less readable. For example, if your site collection is http://[sharepoint]/portal, then you could easily redirect from, say, http://[sharepoint]/portal/urgent/december/readme (which apparently does not exist) to, for instance, http://[sharepoint]/portal/lists/urgentMessages/DispForm.aspx?ID=123

Let’s get started. What we will need is:
1. Empty SharePoint 2010 project in Visual Studio
2. Two features: One Web Application scoped, for adding the web config modification with our HttpModule; second – site collection scoped – for instantiating a configuration list, so that we can have multiple configurable re-directions.
3. List template / instance of configuration list
4. Actual class defining our HttpModule.


Firstly, create a ListTemplate. Make sure it has a display name “UrlRewriter Config” and has two text fields: SourceURL and DestinationURL.
Also create a ListInstance for this template, and make sure it’s Url is set to “Lists/UrlRewriterConfig”. Since this is intended to be a hidden config list you might also remove it from quick launch by specifying attribute OnQuickLaunch=”FALSE” in the ListInstance element.

Secondly, MyRedirectionModule:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public class MyRedirectionModule: IHttpModule
{
    /// <summary>
    /// Http application object.
    /// </summary>
    private HttpApplication application;

    /// <summary>
    /// Dispose.
    /// </summary>
    public void Dispose()
    {
    }

    /// <summary>
    /// Init.
    /// </summary>
    /// <param name=”context”>Http application object.</param>
    public void Init(HttpApplication context)
    {
        application = context;
        context.PreSendRequestHeaders += new EventHandler(context_PreSendRequestHeaders);
    }

    /// <summary>
    /// Pre-send request headers.
    /// </summary>
    /// <param name=”sender”>Sender object.</param>
    /// <param name=”e”>Event arguments.</param>
    void context_PreSendRequestHeaders(object sender, EventArgs e)
    {
        HttpResponse response = application.Response;

        if (response.StatusCode == 404 && response.ContentType.Equals(“text/html”, StringComparison.CurrentCulture))
        {
            string requestedUrl = application.Request.Url.AbsolutePath;
            string[] urlElements = requestedUrl.Split(new char[] { ‘/’ });
            int pageElementIndex = urlElements.Length  1;
            string pageName = urlElements[pageElementIndex].ToString();
            string redirectUrl;

            redirectUrl = VanityRedirect(application.Request.Url.AbsoluteUri);
            if (!string.IsNullOrEmpty(redirectUrl))
            {
                response.Redirect(redirectUrl);
            }
        }
    }

    /// <summary>
    /// Vanity redirect.
    /// </summary>
    /// <param name=”requestedUrl”>Requested url.</param>
    /// <returns>Redirected url.</returns>
    public static string VanityRedirect(string requestedUrl)
    {
        string redirectUrl = string.Empty;

        try
        {
            //look and see if the url is in our redirect list
            //using (SPSite site = new SPSite(SPContext.Current.Site.Url))
            using (SPSite site = new SPSite(requestedUrl))
            {
                SPWeb currentWeb = site.RootWeb;

                Uri partialUrl = new Uri(requestedUrl);
                requestedUrl = partialUrl.AbsolutePath;

                SPList list = currentWeb.Lists[“UrlRewriter Config”];

                if (list != null)
                {
                    SPQuery query = new SPQuery();

                    query.Query = “<Where><Contains><FieldRef Name=\”SourceURL\” /><Value Type=\”Text\”>” + requestedUrl + “</Value></Contains></Where>”;

                    SPListItemCollection items = list.GetItems(query);

                    if (items.Count > 0)
                    {
                        redirectUrl = items[0][“DestinationURL”].ToString();
                    }
                }
            }
        }
        catch
        {
            return string.Empty;
        }
        return redirectUrl;
    }
}

Next comes the Web Application scoped feature:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class UrlRewriterInfrastructureFeatureEventReceiver : SPFeatureReceiver
{
    private struct ModificationEntry
    {
        public string Name;
        public string XPath;
        public string Value;

        public ModificationEntry(string Name, string XPath, string Value)
        {
            this.Name = Name;
            this.XPath = XPath;
            this.Value = Value;
        }
    }

    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
        UpdateWebConfig(webApp);
    }

    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
        SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
        RemoveWebConfig(webApp);
    }

    private void UpdateWebConfig(SPWebApplication webApplication)
    {
        ModificationEntry[] ChildNodeEntries = 
        {                
            new ModificationEntry(“add[@name=’UrlRewriterModule’]”“configuration/system.webServer/modules”string.Format(@”<add name=”“UrlRewriterModule”” type=”“MyNameSpace.MyRedirectionModule, {0}”” />”, Assembly.GetExecutingAssembly().FullName)),            
        };

        foreach (ModificationEntry entry in ChildNodeEntries)
        {
            webApplication.WebConfigModifications.Add(CreateModification(entry.Name, entry.XPath, entry.Value,                                       SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode));
            webApplication.Update();
        }
        webApplication.WebService.ApplyWebConfigModifications();
    }

    private void RemoveWebConfig(SPWebApplication webApplication)
    {
        ModificationEntry[] attributeEntries = 
        {                
            //new ModificationEntry(“mode”, “configuration/system.web/customErrors”,”Off”),
            //new ModificationEntry(“CallStack”, “configuration/SharePoint/SafeMode”,”true”),
        };

        ModificationEntry[] ChildNodeEntries = 
        {                
            new ModificationEntry(“add[@name=’UrlRewriterModule’]”“configuration/system.webServer/modules”string.Format(@”<add name=”“UrlRewriterModule”” type=”“MyNameScpace.MyRedirectionModule, {0}”” />”, Assembly.GetExecutingAssembly().FullName)),  
        };

        foreach (ModificationEntry entry in attributeEntries)
        {
            webApplication.WebConfigModifications.Remove(CreateModification(entry.Name, entry.XPath, entry.Value,                                            SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute));
            webApplication.Update();
        }

        foreach (ModificationEntry entry in ChildNodeEntries)
        {
            webApplication.WebConfigModifications.Remove(CreateModification(entry.Name, entry.XPath, entry.Value,                                            SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode));
            webApplication.Update();
        }
        webApplication.WebService.ApplyWebConfigModifications();
    }

    public SPWebConfigModification CreateModification(string Name, string XPath, string Value, SPWebConfigModification.SPWebConfigModificationType Type)
    {
        SPWebConfigModification modification = new SPWebConfigModification(Name, XPath);
        modification.Owner = “Internet SharePoint Smart 404”;
        modification.Sequence = 0;
        modification.Type = Type;
        modification.Value = Value;
        return modification;
    }
}

And, lastly, just make sure your List template / instance is added to site collection feature.

And, you’re done! Note, when inputting items in the UrlConfigList it’s better to use relative paths! Like, if you have a site collection http://[sharepoint]/portal, then my SourceURL would be for example /portal/something/that/does/not/exist and my DestinationURL will be /portal/Pages/home.aspx

About the anonymous, as mentioned in the topic. Well, it just works for them also, no changes needed!

Hope this will come in handy to someone  🙂

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *