July 2009 - Posts

Mini-Tweek: customErrors in ASP.NET

As of the latest .NET Service Pack (3.5 SP1, 3.0 SP2, 2.0 SP2) there is a new attribute for the <customErrors> element of your web.config, redirectMode.  It has 2 settings, ResponseRedirect (default, legacy functionality), and ResponseRewrite (new functionality).  The ResponseRedirect setting causes the custom error redirection to work just like all of the previous versions where the server performs a redirect and all state (i.e. exception stack) information is lost.  The only way to get around this previously was to add a Server.Transfer() to the Application_Error method of your global.asax, which rendered the <customErrors> section useless as you had to hard code the URL in global.asax.cs.

This new setting cases the server to do a Server.Transfer() to the defaultRedirect page.  This means that the page state (including exceptions) are intact and that you can use the configurability of the <customErrors> section again.  I recently needed this functionality on a project where I'm the lead developer, but the client wants to write most of the SQL code.  Since he is not local to the test server, the RemoteOnly setting doesn't help.  Since this functionality needs to go on the production server as well, I created a custom error page and added just the error text to the page.  It's just enough information to let him know why the site is failing, but doesn't give hackers any useful information.

Posted by Bill Bosacker with no comments

ASP.NET Tweeks (Part 1): PageBase

Modularity is the heart of any application.  This is the first of a multi-part part series of Tweeks that you can use while writing a modular ASP.NET application.  The first module on the list is the use of a PageBase object.  The PageBase class derives from the System.Web.UI.Page class, it should exist in an external components library, and it should replace the System.Web.UI.Page object that every .aspx page derives from.  If the PageBase class, as well as any custom controls, are placed in the web application itself you will run into several issues (especially while debugging) that worst case can cause Visual Studio to crash.  Never place the PageBase class or custom controls in the App_Code folder.

The concept of using a PageBase class has been around for a very long time, but never really got a face lift with the advent of master pages.  Master pages took over some of the functionality of what a typical PageBase class would perform, and required the duplication of common access business tier code.  The next part of this series covers Business Abstraction, to eliminate the duplication of code and to provide a single method of accessing the business tier.  Here is a sample set of PageBase classes w/ direct Business access.  1 for Web Forms, and 1 for Web Content Forms (based on a master page):

using System;
using System.Web.UI;

using BusinessFacade;

/// <summary>
///         Summary description for PageBase.
/// </summary>
/// <typeparam name="TMaster">The master page being used.</typeparam>
public class PageBase<TMaster> : PageBase
      where TMaster : MasterPage
{
      /// <summary>
      ///         Gets the hard typed master page.
      /// </summary>
      public new TMaster Master
      {
            get { return (TMaster)base.Master; }
      }
}

/// <summary>
///         Summary description for PageBase.
/// </summary>
public class PageBase : Page
{
      private CommonDataBF _commonDataBF;
      private UserManagementBF _userManagementBF;


      /// <summary>
      ///         Gets a <see cref="CommonDataBF"/> business object.
      /// </summary>
      public CommonDataBF CommonData
      {
            get
            {
                  if (_commonDataBF == null)
                  {
                        _commonDataBF = new CommonDataBF();
                  }

                  return _commonDataBF;
            }
      }

      /// <summary>
      ///         Gets a <see cref="UserManagementBF"/> business object.
      /// </summary>
      public UserManagementBF UserManagement
      {
            get
            {
                  if (_userManagementBF == null)
                  {
                        _userManagementBF = new UserManagementBF();
                  }

                  return _userManagementBF;
            }
      }
}

To use this, modify the declaration of the code behind page class to derive from this class.  Use the standard PageBase class for normal page forms, and use the generic PageBase class for page content forms (pages that use a master page).  Master pages don't normally need this same type of handling, as there should be a single master page that all other master pages cascade from.  However, you will want your master pages to implement interfaces, which makes it easier to pass-through public properties and methods.  This is an interface that I use, which is inherited by all master pages:

/// <summary>
/// Summary description for IMasterPage.
/// </summary>
public interface IMasterPage
{
    /// <summary>
    /// Gets the <see cref="MembershipUser"/> object for the currently logged in user.
    /// </summary>
    MembershipUser User { get; }

    /// <summary>
    /// Gets the identifier of the currently logged in user.
    /// </summary>
    Guid UserId { get; }

    /// <summary>
    /// Gets or sets the suffix to be added to the HTML page title.
    /// </summary>
    string PageTitleSuffix { get; set; }
}

The parent master obviously is the source of these properties and methods, but here is the pass-through code of a child master page that implements it:

public partial class UserMaster : MasterPage, IMasterPage
{
      /// <summary>
      ///         Gets the <see cref="MembershipUser"/> object for the currently logged in user.
      /// </summary>
      public MembershipUser User
      {
            get { return ((IMasterPage)base.Master).User; }
      }

      /// <summary>
      ///         Gets the identifier of the currently logged in user.
      /// </summary>
      public Guid UserId
      {
            get { return ((IMasterPage)base.Master).UserId; }
      }

      /// <summary>
      ///         Gets or sets the suffix to be added to the HTML page title.
      /// </summary>
      public string PageTitleSuffix
      {
            get { return ((IMasterPage)base.Master).PageTitleSuffix; }
            set { ((IMasterPage)base.Master).PageTitleSuffix = value; }
      }
}

When the master page interfaces and PageBase class are coupled together, you end up with a very nice and easy to use interface with very little duplication of code (the implementation of the master page interfaces on child masters).  You can completely eliminate all duplication of code by declaring a MasterPageBase for each master page that has child master pages, but it may be more difficult to manage and not prudent to do.  Additionally, if you are using user controls, as you should be, interfaces and a UserControlBase class are the best way to pass information back and forth between the page and controls, and even from control to control when using nested user controls.

Posted by Bill Bosacker with no comments