Thursday 6 March 2014

Custom error page for a public facing publishing portal

In the previous Post we created a web application and extended it to have a Anonymous Public Facing Door, that is an entry point that is Read only and Anonymous. At the end of that post we saw a lovely blank 401 unauthorized error page.

Not exactly user friendly, especially when you work in government and you have some crazy conspiracy theorist claiming he's found the web page about selling organs on the black market, when it's really just a bus route some coop student forgot to check back in.

So lets start by creating a new SharePoint Project

Hit Ok,


Make sure to select Farm solution and hit finish.

Now you Have your SharePoint Project, next what your going to do is create a class library, right click on your solution->add->New Project.

Select Windows Templates, pick Class Library, give your project a name and hit OK.

In the above I accidentally have framework 4.5 selected, for SharePoint 2010 you have to have 3.5 so make the change here or right click on your project and change it there.

Next your going to have to add a reference to the System.Web.dll. In your solution explorer right click on references and hit add References.

with that added go to your class, by default the file and class should be called class1.cs change that to something more appropriate, it's common to suffix whatever you call it with the word module.

next implement the IHttpModule interface,

using System;
using System.Web;

namespace ErrorRedirect
{
    public class ErrorSwapModule : IHttpModule
    {
        #region IHttpModule Members

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        public void Init(HttpApplication context)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}


right click on IHttpModule and implement it implicitly, that'll get you the init and dispose functions. I wrap them in a region this is just for decoration and makes it easer to manage large classes. how you organize your code is really up to you and your team, you just have to pick what works for you and stick to it.

next we add a new event handler to our context to handle The PreSendRequestContent event.

using System;
using System.Web;

namespace ErrorRedirect
{
    public class ErrorSwapModule : IHttpModule
    {
        private HttpApplication _webApp;

        #region IHttpModule Members
        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public void Init(HttpApplication context)
        {
            _webApp = context;
            _webApp.PreSendRequestContent +=
                new EventHandler(WebApp_PreSendRequestContent);
        }

        #endregion

        private void WebApp_PreSendRequestContent(object sender, EventArgs e)
        {
            HttpResponse response = _webApp.Response;

            string message = string.Empty;

            switch (response.StatusCode)
            {
                case 401:
                    this._webApp.Response.Clear();
                    this._webApp.Response.Write(
                        @"<html>
                            <head />
                            <body>
                                This is a Custom 401
                            </body>
                        </html>");
                    break;
            }
        }
    }
}


this is where we check the status code of our response and then redirect our user to where ever we feel is appropriate.

Now we've done a lot of work, but we're not done yet, we still have to register this module in our GAC, and to do this your project has to be signed, what this means is a bit out of the scope of this post but think of it as giving it a unique secure-ish identifier.

To sign your project, right click on it and go to properties, on the left select the signing tab

now from the drop down select browse, next navigate to the SharePoint Project we created earlier at the start of this post and select that key.snk file

with that selected our solution is now signed, now we are going to need our modules public Token, the public token is an abstraction of the public key, if you don't know what that is don't worry about it too much it has to do with asynchronous Encryption and a privet-public key pair again outside of the scope of this, but you know enough to look it up yourself.

Now when it comes to getting our public Token, the best way is in this post, don't worry it works in VS 2012 too.


when you run your tool, you should get something like the above, but with a different public key and token. make note of this Public Key Token for later. In my case it's 06ae58ed1b6fa751 in your case it'll be something different.

Now we have enough to register our Module in our Web Config, now you should never modify your web config by hand, but to keep this post shorter we are going to break that rule. open up the web config for the extension of your site. In the previous post we created a web app with port 3333, then we extended it on port 4444. Make sure you make your modification for port 4444.

Your web config will be "C:\inetpub\wwwroot\wss\VirtualDirectories\4444"


now open that up in your favourite editor, personally I like notepad++, but that's me.
look for the line
<modules runAllManagedModulesForAllRequests="true">
it should be around line 450 in the webconfig, now directly after it your going to append something along the lines of
<add name="ErrorRedirect"

    type="ErrorRedirect.ErrorSwapModule,
ErrorRedirect
,
Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=06ae58ed1b6fa751"
/>

Just match the above with the below

using System;
using System.Web;

namespace ErrorRedirect
{
    public class ErrorSwapModule: IHttpModule
    {
        ...
    }

}

As for the Version number and Culture, you set that in the project properties
and we talked above how to get the Public key token.

with that change made in your webconfig, deploy your project and try to navigate to your settings folder of your extended web app that's the one with 4444 as a port number.


still not the greatest but you can at least customize it, now you could deploy a much more robust html page, read it in using some sort of string reader, and write that to your response, just make sure all of your assets, such as css and images are in a publicly available library, ie not your layouts folder.