Custom Error Page Adapter

February 12 2010 20 comments

If you are working with SharePoint 2010, see this article by Todd Carter:
An Expected Error Has Occurred
Mike has also written a nice article of the other options:
SharePoint 2010 Custom Error Messages for Public Facing Deployments

SharePoint doesn’t have an out-of-the-box support for custom error pages. Or at least it’s a little bit problematic to implement custom error pages in SharePoint context. One problem is, that SharePoint has an HttpModule that handles error scenarios in SharePoint context. If you want to implement custom error pages with your own HttpModule, you should be careful to have it before SharePoint’s modules in web.config. Because SharePoint’s HttpModule handles error scenarios in SharePoint context, you cannot use ASP.NET’s <customErrors>-tag in web.config (the redirects won’t work). Another way to implement custom error pages in SharePoint is to use control adapter. In this article I will show you how to implement one.

The first part of this solution is a SharePoint Feature:

<?xml version="1.0" encoding="utf-8"?>
<Feature
    Id="6EE4DA6F-48A4-421b-8455-318E103A69F7"
    Title="$Resources:Meteoriitti,feature_adapters_errorpage_title;"
    Description="$Resources:Meteoriitti,feature_adapters_errorpage_desc;"
    Version="1.0.0.0"
    Scope="WebApplication"
    ImageUrl="Meteoriitti/Feature.gif"
    ActivateOnDefault="False"
    AutoActivateInCentralAdmin="False"
    ReceiverAssembly="Sininen.Meteoriitti.SharePoint.Web.UI..."
    ReceiverClass="...ErrorPageControlAdapterFeatureReceiver"  
    xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
        <ElementFile Location="meteoriitti_error.browser" />
    </ElementManifests>
</Feature>

And the associated browser-file:

<?xml version="1.0" encoding="utf-8" ?>
<browsers>
  <browser refID="Default">
    <controlAdapters>
      <adapter
       controlType="
         Microsoft.SharePoint.ApplicationPages.ErrorPage,
         Microsoft.SharePoint.ApplicationPages, Version=12.0.0.0,
         Culture=neutral, PublicKeyToken=71e9bce111e9429c"

       adapterType="
         Sininen.Meteoriitti.SharePoint.Web.UI.ErrorPageControlAdapter,
         Sininen.Meteoriitti.SharePoint.Web.UI, Version=1.0.0.0,
         Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx" />

      </controlAdapters>
  </browser>
</browsers>

When the above Feature is activated on a web application level, the feature receiver instantiates a new timer job to copy the meteoriitti_error.browser in web application’s App_Browsers directory (see timer jobs). The timer job is used to handle farm deployment scenario where there are multiple web front ends. You can also do it manually if you like, but that’s not recommended as all manual work makes expanding SharePoint Farm more difficult.

Now we have a control adapter in place, the code for that control adapter is the last missing piece of the puzzle, so here it goes:

using System;
using System.Web;
using System.Web.UI.Adapters;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Utilities;

namespace Sininen.Meteoriitti.SharePoint.Web.UI
{
    public class ErrorPageControlAdapter : PageAdapter
    {
        protected override void OnInit(EventArgs e)
        {
            var context = HttpContext.Current;
            var exceptions = context.AllErrors;
            var response = Page.Response;
           
            if (exceptions == null || exceptions.Length == 0) return;
           
            using (var web = SPContext.Current.Site.RootWeb)
            {
                var url = PublishingWeb.IsPublishingWeb(web)
                    ?
                    SPUrlUtility.CombineUrl(
                    web.ServerRelativeUrl,
                    string.Format("{0}/Error.aspx",
                    PublishingWeb.GetPagesListName(web)))
                    :
                    SPUrlUtility.CombineUrl(
                    web.ServerRelativeUrl,
                    "Error.aspx");
           
                if (string.IsNullOrEmpty(url)) return;
                if (!web.GetFile(url).Exists) return;
                if (url.Equals(context.Request.FilePath,
                    StringComparison.OrdinalIgnoreCase)) return;
           
                // This might be a good place to log the errors.
           
                response.Redirect(url);
            }
        }
    }
}

The above code redirects a browser to RootWeb’s Error.aspx, or in case of publishing web to Pages/Default.aspx (localization is handled). That’s why you should provision the appropriate file to root web. The code also check if the file (Error.aspx) exists before redirecting. If it doesn’t exists the out-of-the-box error page is displayed. Also if there are errors in the error page, the code falls back to out-of-the-box error page.

Well, there is still one thing you need to take care of: the right HTTP status code! Here is an example of publishing error page’s code behind:

using Microsoft.SharePoint.Publishing;

namespace Sininen.Meteoriitti.SharePoint.Web.UI
{
    public class PublishingErrorPage : PublishingLayoutPage
    {
        public PublishingErrorPage()
        {
            Init += PublishingErrorPage_Init;
        }
        void PublishingErrorPage_Init(object sender, System.EventArgs e)
        {
            Response.StatusCode = 500;
            Response.StatusDescription = "Internal Server Error";
        }
    }
}

One great feature of this implementation is that you can still enable debug mode just as you usually do:

<SafeMode
    MaxControls="200"
    CallStack="true"
    DirectFileDependencies="10"
    TotalFileDependencies="50"
    AllowPageLevelTrace="true">

And:

<customErrors mode="Off" />

The only downside is that we need to use redirect. I tried to use rewrite, but it didn’t work. So what do you think about this?

Popularity: 4% [?]

20 comments to “Custom Error Page Adapter”

  1. Bart says:

    Hi Aapo,

    I went through your article and I think this is what I have been looking for but I have no idea how I can implement it in my envoirment. Can you provide step-by-step insctructions how to do it? Thanks.

  2. Aapo Laakkonen says:

    Hi Bart,

    Here you go (the short path):

    1. Create an error.browser file like what I did in the article, and put it inside:
    C:\Inetpub\wwwroot\wss\VirtualDirectories\80\App_Browsers (just an example)
    (and change adapterType to point to your class)
    2. Build a DLL with the adapter code I provided above and put the DLL in GAC
    3. Create Error.aspx inside root webs pages library
    4. Run iisreset
    5. Create error in your web part, user control etc. (throw new Exception(“Testing Error Page Implementation”);) and see if its working and redirecting you to Error.aspx.

    Regards
    Aapo

    PS. please don’t hesitate to ask more if I still wasn’t clear enough.

  3. Aapo Laakkonen says:

    Bart,

    Please also note that the adapter will only work if you get to SharePoint’s custom error page with some exceptions or errors: /_layouts/Error.aspx (this is what for the adapter is).

    Please also note that you have to have,
    <customErrors mode=”On” />, in your web.config.

    Try to go to http://yourwebapp/_layouts/Error.aspx with adapter enabled and see if the adapter is executed in debugger. It will return at this point, because there wasn’t really a error:
    if (exceptions == null || exceptions.Length == 0) return;

  4. Shakti Srivastav says:

    Hi Aapo,

    I am implementing the samething:
    1. Placed a .browser file and plcaed it under the App_browsers dierctory of the web application.
    2. created a feature that tells SharePoint that about the browser file.
    3. Created a class library that just redirects to http://www.google.com

    But nothing worked…even there is no error getting displayed…i m just getting seeing the Error page as i was seeing previously…neither i can debug the code using the breakpoint….

    i even added a safe control entry for the class library…even then the same story…

    Is there any thing else i need to look upon…or i m missing something…

    Thanks,
    Shakti
    Email – shakti.ietk.85@gmail.com

  5. Aapo Laakkonen says:

    Hi All,

    I was in contact with Shakti, and we figured out that there might be some problems with having separate browser-file in App_Browsers directory. If you cannot get this to work with separate browser-file, please try to include the content of that browser-file inside compat.browser.

    I still don’t know why Shakti’s SharePoint couldn’t pick up the new browser file (you can try to touch compat.browser, but I think that IISRESET will do just fine). The point is that it’s working correctly in our environment, and not in Shakti’s.

    So, stay tuned, maybe we can figure out the actual reason why this didn’t work in Shakti’s environment in a first place.

  6. Okko Oulasvirta says:

    Hi Aapo,

    thanks for clear and informative article. Interesting approach I need to test this one later… Two small suggestions: you may want to use Server.Transfere instead of Response.Redirect to redirect user to your custom error page. Maybe also some logic to verify that the exception is not 401, 403 or 404 related in which case you may want to let SharePoint handle the request/error or handle those separately.

    Cheers and greetings to Tavikukko!

  7. Aapo Laakkonen says:

    Hi Okko,

    Yes I am aware of Server.Transfer, but you cannot use it to transfer to a publishing page that is served by SharePoint. But I agree that it would be so much better if it worked. If you can get it to work, please let me know about it.

    For 404 pages I have a different approach. That isn’t handled with this adapter. Maybe I will blog about it later. For unauthorized scenarios you can handle it differently, maybe a Control Adapter for layouts/AccessDenied.aspx?

    Thanks for your comments.

  8. Boban Miksin says:

    If you are looking on how to set the custom error page in MOSS please check my blog post http://blog.vegaitsourcing.rs/2010/03/sharepoint-custom-error-page.html

  9. Aapo Laakkonen says:

    Boban,

    I think you are right. But there are several problems in your approach. As you see, there is this thing called SPUtility.TransferToErrorPage, and that’s hard coded to “~/_layouts/error.aspx”. You see the problem here? So it doesn’t have to be exception that’s falling through when you end up in “~/_layouts/error.aspx”. That’s why I have this adapter, so that I’m always absolutely sure that on a public web site normal SharePoint error page is never displayed.

    Your aproach doesn’t handle this:
    SPUtility.TransferToErrorPage(“this totally skips the customErrors and defaultRedirect setting in web.config, and it doesn’t matter how you configure CallStack setting.”);

  10. James says:

    Hi Aapo,

    I’ve been researching for this custom error page issue for more than 8 hours and finally found your post. I am trying to redirect the page for 404 and 500 error, and have tried the redirect in the CustomErrors sectoin in web.config, but found it cannot catch the 404 and 500 error in Sharepoint.

    How do you know where does the error generated? How can I check if there are from the SPUtility.TransferToErrorPage?

    Basically, I had 404 error when try to access a non-existing site in URL, and got 500 error when tried “Open in Excel” on an Excel webpart component after the session times out.

    Thank you and great post.

  11. Venkat says:

    Hi Aapo,

    This approach does not seems tobe working for 404, 500 errors. For me 404 Errors custom page not found redirection is required, Any different approach for 404 pages?

  12. Aapo Talvensaari says:

    Hi Venkat,

    For 404 errors, I think it’s best to just stick with this:
    http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spwebapplication.filenotfoundpage.aspx

    It’s not an optimal solution, but there hardly ever is “optimal” with SharePoint.

  13. emad says:

    Hi, nice idea and well done. I wnat a way to capture server error 500 and redirected to another page .. any ideas??

  14. Shakti says:

    Hi Emad,

    I think if you have the references of the error page…then you can get the error description, code etc..

    Regards,
    Shakti

  15. “Hi, nice idea and well done”

  16. Allan says:

    I have a need to provide a custom message to users when they attempt to upload a blocked file type. Can this solution be adapted to fit that need? I am unsure if there is an HTML Status code sent with that type of an error or if that is even considered an error. Any help is appreciated.

  17. [...] Access denied Page. Another option is using a control adapter as per Aapo Talvensaari’s post Custom Error Page Adapter although i’d be a little wary implementing the functionality in this manner. My favourite [...]

  18. There are definitely a lot of particulars like that to take into consideration. That is a nice level to carry up. I offer the ideas above as basic inspiration however clearly there are questions just like the one you deliver up the place the most important factor can be working in sincere good faith. I don?t know if greatest practices have emerged around things like that, however I’m certain that your job is clearly identified as a good game. Each boys and girls feel the influence of just a moment’s pleasure, for the rest of their lives.

  19. Normally I do not read post on blogs, however I wish to say that this write-up very compelled me to check out and do it! Your writing taste has been surprised me. Thank you, quite great post.

  20. TetrisDeluxe says:

    Hy Aapo, great article. Currently I try to adapt this with SharePoint 2013. The adapter is now working fine for me, but I have to change the compat.browser file manually. I don’t understand how to deploy to APP_Browser folder. Can you described this part again in more detail?

Leave a Reply