11

What cool functionality and methods do you add to your ASP.net BasePage : System.Web.UI.Page classes?

Examples

Here's something I use for authentication, and I'd like to hear your opinions on this:

protected override void OnPreInit(EventArgs e)
{
    base.OnPreInit(e);

    // Authentication code omitted... Essentially same as below.

    if (_RequiresAuthentication && !(IsAuthorized))
    {
        RespondForbidden("You do not have permissions to view this page.", UnauthorizedRedirect);
        return;
    }
}

// This function is overridden in each page subclass and fitted to each page's
// own authorization requirements.
// This also allows cascading authorization checks,
// e.g: User has permission to view page? No - base.IsAuthorized - Is user an admin?
protected virtual bool IsAuthorized
{
    get { return true; }
}

My BasePage class contains an instance of this class:

public class StatusCodeResponse {

    public StatusCodeResponse(HttpContext context) {
        this._context = context;
    }

    /// <summary>
    /// Responds with a specified status code, and if specified - transfers to a page.
    /// </summary>
    private void RespondStatusCode(HttpContext context, System.Net.HttpStatusCode status, string message, string transfer)
    {
        if (string.IsNullOrEmpty(transfer))
        {
            throw new HttpException((int)status, message);
        }

        context.Response.StatusCode = (int)status;
        context.Response.StatusDescription = message;
        context.Server.Transfer(transfer);
    }

    public void RespondForbidden(string message, string transfer)
    {
        RespondStatusCode(this._context, System.Net.HttpStatusCode.Forbidden, message, transfer);
    }

    // And a few more like these...

}

As a side note, this could be accomplished using extension methods for the HttpResponse object.

And another method I find quite handy for parsing querystring int arguments:

public bool ParseId(string field, out int result)
{
    return (int.TryParse(Request.QueryString[field], out result) && result > 0);
}
GeReV
  • 3,195
  • 7
  • 32
  • 44
  • 2
    These are all methods that **should not** be in the basepage, but in some helper class – Jan Jongboom Jan 14 '10 at 15:07
  • @Jan, mind explaining why for those that don't understand. – mxmissile Jan 14 '10 at 15:59
  • Check my answer. Helper classes are intended to provide parsing of things, playing with HttpContext can/should also be in another helper class. Group methods there per use, and don't stuff them together in a baseclass. – Jan Jongboom Jan 14 '10 at 17:45

9 Answers9

5
  • Session related stuff, some complex object in the BasePage that maps to a session, and expose it as a property.
  • Doing stuff like filling a crumble pad object.

But most important: do not make your basepage into some helper class. Don't add stuff like ParseId(), that's just ridiculous.


Also, based on the first post: make stuff like IsAuthorized abstract. This way you don't create giant security holes if someone forgets that there is some virtual method.

Jan Jongboom
  • 26,598
  • 9
  • 83
  • 120
  • Well, the ParseId() function is really helpful. But I suppose you're right about it being a helper function and not belonging there... – GeReV Jan 14 '10 at 15:16
  • +1 so many base classes are used to slap a bunch of helper methods in – Charlie Jan 14 '10 at 15:56
  • Making IsAuthorized abstract was taken into consideration, but making it virtual was intended. When subclassing an AdminBasePage, I override it to something like "return User.IsAdmin;". This is used for the cascading permission checks mentioned in the comment. – GeReV Jan 14 '10 at 18:43
  • 1
    +1 for the Session recommendation. I wish I could give a second point for your second pieces of advice. I added a few "utilities" when I first created a base page ("hey, this makes it easy") but came to my senses as my architecture began to evolve. This is a case of "you may not understand why - but you really shouldn't do this." – Mark Brittingham Feb 02 '10 at 17:12
3
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace MySite
{
    /// <summary>
    /// Base class with properties for meta tags for content pages
    /// http://www.codeproject.com/KB/aspnet/PageTags.aspx
    /// http://weblogs.asp.net/scottgu/archive/2005/08/02/421405.aspx
    /// </summary>
    public partial class BasePage : System.Web.UI.Page
    {
        private string keywords;
        private string description;


        /// <SUMMARY>
        /// Gets or sets the Meta Keywords tag for the page
        /// </SUMMARY>
        public string Meta_Keywords
        {
            get
            {
                return keywords;
            }
            set
            {
                // Strip out any excessive white-space, newlines and linefeeds
                keywords = Regex.Replace(value, "\\s+", " ");
            }
        }

        /// <SUMMARY>
        /// Gets or sets the Meta Description tag for the page
        /// </SUMMARY>
        public string Meta_Description
        {
            get
            {
                return description;
            }
            set
            {
                // Strip out any excessive white-space, newlines and linefeeds
                description = Regex.Replace(value, "\\s+", " ");
            }
        }


        // Constructor
        // Add an event handler to Init event for the control
        // so we can execute code when a server control (page)
        // that inherits from this base class is initialized.
        public BasePage()
        {
            Init += new EventHandler(BasePage_Init); 
        }


        // Whenever a page that uses this base class is initialized,
        // add meta keywords and descriptions if available
        void BasePage_Init(object sender, EventArgs e)
        {
            if (!String.IsNullOrEmpty(Meta_Keywords))
            {
                HtmlMeta tag = new HtmlMeta();
                tag.Name = "keywords";
                tag.Content = Meta_Keywords;
                Header.Controls.Add(tag);
            }

            if (!String.IsNullOrEmpty(Meta_Description))
            {
                HtmlMeta tag = new HtmlMeta();
                tag.Name = "description";
                tag.Content = Meta_Description;
                Header.Controls.Add(tag);
            }
        }
    }
}
IrishChieftain
  • 15,108
  • 7
  • 50
  • 91
3

Along with the metadata already mentioned (mostly obsolete in ASP.NET 4.0 with the new Page.MetaDescription and Page.MetaKeywords properties), I've also had methods to add other header links to my page such as specific ones for adding page specific CSS, or things like cannonical links, RSS links, etc:

/// <overloads>
/// Adds a CSS link to the page. Useful when you don't have access to the
///  HeadContent ContentPlaceHolder. This method has 4 overloads.
/// </overloads>
/// <summary>
/// Adds a CSS link.
/// </summary>
/// <param name="pathToCss">The path to CSS file.</param>
public void AddCss(string pathToCss) {
    AddCss(pathToCss, string.Empty);
}

/// <summary>
/// Adds a CSS link in a specific position.
/// </summary>
/// <param name="pathToCss">The path to CSS.</param>
/// <param name="position">The postion.</param>
public void AddCss(string pathToCss, int? position) {
    AddCss(pathToCss, string.Empty, position);
}

/// <summary>
/// Adds a CSS link to the page with a specific media type.
/// </summary>
/// <param name="pathToCss">The path to CSS file.</param>
/// <param name="media">The media type this stylesheet relates to.</param>
public void AddCss(string pathToCss, string media) {
    AddHeaderLink(pathToCss, "text/css", "Stylesheet", media, null);
}

/// <summary>
/// Adds a CSS link to the page with a specific media type in a specific
/// position.
/// </summary>
/// <param name="pathToCss">The path to CSS.</param>
/// <param name="media">The media.</param>
/// <param name="position">The postion.</param>
public void AddCss(string pathToCss, string media, int? position) {
    AddHeaderLink(pathToCss, "text/css", "Stylesheet", media, position);
}

/// <overloads>
/// Adds a general header link. Useful when you don't have access to the
/// HeadContent ContentPlaceHolder. This method has 3 overloads.
/// </overloads>
/// <summary>
/// Adds a general header link.
/// </summary>
/// <param name="href">The path to the resource.</param>
/// <param name="type">The type of the resource.</param>
public void AddHeaderLink(string href, string type) {
    AddHeaderLink(href, type, string.Empty, string.Empty, null);
}

/// <summary>
/// Adds a general header link.
/// </summary>
/// <param name="href">The path to the resource.</param>
/// <param name="type">The type of the resource.</param>
/// <param name="rel">The relation of the resource to the page.</param>
public void AddHeaderLink(string href, string type, string rel) {
    AddHeaderLink(href, type, rel, string.Empty, null);
}

/// <summary>
/// Adds a general header link.
/// </summary>
/// <param name="href">The path to the resource.</param>
/// <param name="type">The type of the resource.</param>
/// <param name="rel">The relation of the resource to the page.</param>
/// <param name="media">The media target of the link.</param>
public void AddHeaderLink(string href, string type, string rel, string media)
{
    AddHeaderLink(href, type, rel, media, null);
}

/// <summary>
/// Adds a general header link.
/// </summary>
/// <param name="href">The path to the resource.</param>
/// <param name="type">The type of the resource.</param>
/// <param name="rel">The relation of the resource to the page.</param>
/// <param name="media">The media target of the link.</param>
/// <param name="position">The postion in the control order - leave as null
/// to append to the end.</param>
public void AddHeaderLink(string href, string type, string rel, string media, 
                          int? position) {
  var link = new HtmlLink { Href = href };

  if (0 != type.Length) {
    link.Attributes.Add(HtmlTextWriterAttribute.Type.ToString().ToLower(), 
                        type);
  }

  if (0 != rel.Length) {
    link.Attributes.Add(HtmlTextWriterAttribute.Rel.ToString().ToLower(),
                        rel);
  }

  if (0 != media.Length) {
    link.Attributes.Add("media", media);
  }

  if (null == position || -1 == position) {
    Page.Header.Controls.Add(link);
  }
  else
  {
    Page.Header.Controls.AddAt((int)position, link);
  }
}
Zhaph - Ben Duguid
  • 26,785
  • 5
  • 80
  • 117
1

Culture initialization by overriding InitializeCulture() method (set culture and ui culture from cookie or DB).

Some of my applications are brandable, then here I do some "branding" stuff too.

Tadas Šukys
  • 4,140
  • 4
  • 27
  • 32
1

I use this methot and thanks for yours,

/// <summary>
        /// Displays the alert.
        /// </summary>
        /// <param name="message">The message to display.</param>
        protected virtual void DisplayAlert(string message)
        {
            ClientScript.RegisterStartupScript(
                            GetType(),
                            Guid.NewGuid().ToString(),
                            string.Format("alert('{0}');", message.Replace("'", @"\'")),
                            true
                        );
        }

        /// <summary>
        /// Finds the control recursive.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns>control</returns>
        protected virtual Control FindControlRecursive(string id)
        {
            return FindControlRecursive(id, this);
        }

        /// <summary>
        /// Finds the control recursive.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="parent">The parent.</param>
        /// <returns>control</returns>
        protected virtual Control FindControlRecursive(string id, Control parent)
        {
            if (string.Compare(parent.ID, id, true) == 0)
                return parent;

            foreach (Control child in parent.Controls)
            {
                Control match = FindControlRecursive(id, child);

                if (match != null)
                    return match;
            }

            return null;
        }
Florim Maxhuni
  • 1,421
  • 1
  • 17
  • 35
1

Putting authorization code in a base page is generally not a good idea. The problem is, what happens if you forget to derive a page that needs authorization from the base page? You will have a security hole.

It's much better to use an HttpModule, so that you can intercept requests for all pages, and make sure users are authorized even before the HttpHandler has a chance to run.

Also, as others have said, and in keeping with OO principles, it's better to only have methods in your base page that actually relate to the Page itself. If they don't reference "this," they should probably be in a helper class -- or perhaps be extension methods.

RickNZ
  • 18,448
  • 3
  • 51
  • 66
  • That's an interesting suggestion. Could you give an example as to how an authorization HttpModule would work? – GeReV Jan 17 '10 at 11:52
  • For example, you could put all pages that require authorization in a certain folder. Then, in an AuthenticateRequest event handler in an HttpModule, check to see if the current URL is in that folder (or a subfolder). If so, then check to see if the user is authorized. If not, then issue a redirect to the login page. – RickNZ Jan 17 '10 at 23:33
0

Please refer Getting page specific info in ASP.Net Base Page

public abstract string AppSettingsRolesName { get; }

List<string> authorizedRoles = new List<string>((ConfigurationManager.AppSettings[AppSettingsRolesName]).Split(','))
if (!authorizedRoles.Contains(userRole))
{
Response.Redirect("UnauthorizedPage.aspx");
}

In derived Page

public override string AppSettingsRolesName 
{
  get { return "LogsScreenRoles"; }
}
Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418
0

I inherit from System.Web.UI.Page when I need certain properties and every page. This is good for aweb application that implements a login. In the membership pages I use my own base class to get access to Properties like UserID, UserName etc. These properties wrap Session Variables

citronas
  • 19,035
  • 27
  • 96
  • 164
0

Here are some examples (sans code) that I use a custom base class for:

  1. Adding a page filter (e.g. replace "{theme}" with "~/App_Theme/[currentTheme]"),
  2. Adding a Property and handling for Auto Titling pages based upon Site Map,
  3. Registering specialized logging (could probably be redone via different means),
  4. Adding methods for generalized input(Form/Querystring) validation, with blanket redirector: AddRequiredInput("WidgetID", PageInputType.QueryString, typeof(string)),
  5. Site Map Helpers, allowing for things like changing a static "Edit Class" into something context related like "Edit Fall '10 Science 101"
  6. ViewState Helpers, allowing me to register variable on the page to a name and have it automatically populate that variable from the viewstate or a default, and save the value back out to the viewstate at the end of the request.
  7. Custom 404 Redirector, where I can pass an exception or message (or both) and it will go to a page I have predefined to nicely display and log it.

I personally like #5 the most because a) updating the SiteMap is ugly and I prefer not to have clutter the page, making it more readable, b) It makes the SiteMap much more user friendly.

,

Graham
  • 56
  • 2