Archive

Posts Tagged ‘asp.net’

ActionFilters in MVC

October 20th, 2009 No comments

I’ve been working on two fairly large MVC-projects lately. ImagerGallery.Net and a webshop-application suitably named BeaverShop. While working on those projects I’ve realized that I just love being able to use ActionFilters in MVC. Basically, they are attributes that you append to action-methods. They can be used to validate input, parse data into more suitable structures, check security and lots more.

One of my most used filters in both ImagerGallery and BeaverShop is one I named PagingAttribute which I thought I should show to you today.

Any ActionFilter need to inherit the ActionFilterAttribute and implement at least one of the following methods: OnActionExecuting, OnActionExecuted, OnResultExecuting or OnResultExecuted. They are all called during different stages of the execution of an action-method and can be used for various things. The one I’ll be using is OnActionExecuting since it is executed before the action so that I can pass extra arguments to the action.

The code is quite easy to understand. There is only one property to be set (except for the default property Order which is defined in ActionFilterAttribute) which is which field to be parsed. I check if the field is null or empty, if not ten I use a regular expression to see check if the selected field ends with “page” and a numerical value. The numerical value is then returned as a argument for the action called “pageNo”. This value is defaulted to 1 to make sure that we always get a number for our page (and not specifying anything should mean the first page). In my case I also want the field to be modified so it doesn’t contain the “page” part but that might not be suitable for everyone. If not, then you can easily remove that part by commenting the line looking like this: filterContext.ActionParameters[Field] = id;

using System;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;

namespace BeaverShopMvc.Attributes
{
    /// <summary>
    /// An ActionFilter to parse a page number from a field value
    /// </summary>
    public class PagingAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// The name of the field to parse
        /// </summary>
        public string Field { get; set; }

        // A compiled regular expression to speed things up
        private static Regex _pageRegex = new Regex("page(?<PageNo>\\d+)$", RegexOptions.CultureInvariant | RegexOptions.Compiled);

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            string id = filterContext.ActionParameters[Field] as string;
            Int32 pageNo = 1;

            // We never need an ending / so remove it
            id = id.TrimEnd('/');

            // Check so that the field isn't empty and while at it also that it
            // contains "page"
            if (!string.IsNullOrEmpty(id) && id.ToLowerInvariant().Contains("page"))
            {
                Match match = _pageRegex.Match(id);
                if (match.Success)
                {
                    pageNo = Convert.ToInt32(match.Groups["PageNo"].Value);
                    if (id.Contains("/"))
                        id = id.Substring(0, id.LastIndexOf('/'));
                    else
                        id = string.Empty;
                }
            }

            filterContext.ActionParameters["pageNo"] = pageNo;
            // The line below replaces the old field value with a
            // new one without the "page" part
            filterContext.ActionParameters[Field] = id;

            base.OnActionExecuting(filterContext);
        }
    }
}

Using the attribute is really simple and the great thing about this is that it is easily reusable on any actions you need it to. Below is a little sample of it in action.

        [Paging(Field = "id&")]
        public ActionResult ListProducts(string id, Int32 pageNo)
        {
            return View();
        }

I have a few more filters that I really like as well (AkismetValidatorAttribute for example) that I might share some other day. I’m still learning a lot about working with MVC and will keep you posted with what other interesting things I can find.

Compacting CSS on-the-fly

July 2nd, 2009 No comments

I’m currently doing quite a lot of work on my ny MVC-version of Imager Gallery. I still have a lot to do before I have anything to release (or even use myself) though but I thought I could release a few snippets that I’ve found useful.

First out is a HttpHandler that will process .css-files and remove any unused whitespace from the file before sending it to the client. I’ve been meaning to write this for a long time but never really had the time until someone posted a link about it over at ASPSidan.se showing their own implementation. Reading up on his post about it I found that it was based on a solution by Sam Collett. Both implementations lacked a few things that I wanted to have so I sat down with Sams code and started improving it, mainly making it a work as a handler for .css-files instead of a Generic Handler wanting a querystring but also adding caching and compiling the regular expression as a static member.

To use this, just pop it in a .cs-file in your your App_Code folder and add it to your web.config (as described in the comments for the class). You also need to configure your IIS to route .css-files through the asp.net process to make it run (not needed under IIS7 or IIS6 configured for MVC). Hope you find it useful!

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;

namespace HttpHandlers
{
    /// &lt;summary&gt;
    /// Handler for compacting css-files on the fly
    /// Based on solution by Sam Collett found at
    /// http://webdevel.blogspot.com/2007/09/csscompact-webhandler-for-shrinking-css.html
    ///
    /// Needs to be added to web.config under System.Web/httpHandlers like this
    /// &lt;add verb=&quot;GET,HEAD&quot; path=&quot;*.css&quot; type=&quot;HttpHandlers.CssCompactHandler&quot;/&gt;
    ///
    /// For debuing purposes you can append a querystring whenbrowsing to your .css-file
    /// to skip the compacting. It would look like this:
    /// http://mySite/Content/Site.css?noCompact=1
    /// &lt;/summary&gt;
    public class CssCompactHandler : IHttpHandler
    {
        // Staticly compiled regex to avoid compiling it each request
        private static Regex RegexRemoval = new Regex(@&quot;^\s+|/\*([^*\\\\]|\*(?!/))+\*/|\r|\n|\t&quot;, RegexOptions.Multiline | RegexOptions.Compiled);

        public void ProcessRequest(HttpContext context)
        {
            HttpResponse Response = context.Response;
            HttpRequest Request = context.Request;
            Cache Cache = context.Cache;

            FileInfo fileInfo = new FileInfo(Request.PhysicalPath);
            if (!fileInfo.Exists)
            {
                Response.StatusDescription = &quot;The server has not found anything that matches the requested URI.&quot;;
                Response.StatusCode = 404;
                Response.End();
            }

            Response.ContentType = &quot;text/css&quot;;
            Response.Cache.SetLastModified(fileInfo.LastWriteTime);

            // If we are in debugmode (set in web.config) then
            // we dont want to cache the css since we are probably
            // still in development
            if (context.IsDebuggingEnabled)
            {
                Response.Cache.SetCacheability(HttpCacheability.NoCache);
            }
            else
            {
                Response.Cache.SetCacheability(HttpCacheability.Public);
                Response.Cache.SetExpires(DateTime.Now.AddDays(1));
            }

            // HEAD requests only need the headers so we skip the
            // content here
            if (Request.HttpMethod != &quot;HEAD&quot;)
            {
                string cacheName = string.Format(&quot;CssCompactHandlerCache_{0}&quot;, fileInfo.FullName);
                string cacheValue = Cache[cacheName] as string;
                if (cacheValue != null &amp;&amp; Request.QueryString[&quot;noCompact&quot;] == null)
                {
                    Response.Write(cacheValue);
                    Response.End();
                }

                using (StreamReader cssStream = fileInfo.OpenText())
                {
                    string cssContent = cssStream.ReadToEnd();
                    if (Request.QueryString[&quot;noCompact&quot;] == null)
                    {
                        cssContent = RegexRemoval.Replace(cssContent, &quot;&quot;);

                        // Set up a CacheDependency on the css-file,
                        // this way the cache will be invalidated if
                        // we change the file
                        CacheDependency dependency = new CacheDependency(fileInfo.FullName);
                        Cache.Add(string.Format(&quot;CssCompactHandlerCache_{0}&quot;, fileInfo.FullName), cssContent, dependency, DateTime.Now.AddYears(1), Cache.NoSlidingExpiration, CacheItemPriority.AboveNormal, null);
                    }
                    Response.Write(cssContent);
                }
            }
        }

        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }
}

ASP.Net and metadata

December 19th, 2008 No comments

Building great websites have several parts to it. First of all you need a great idea of what to fill the site with, then you’ll need some great technology in the background (sloppy code can achieve lots of great things aswell, but you won’t want to go in there and add things afterwards) and then a great presentation to the users of your site. In this article I will touch two parts of this, the technology (ASP.Net) and the presentation (metadata).

First of all I just want to state that I’m not a believer in all the SEO crap thats out there. I simply believe that if you have a great site that people visit and link to it will rank higher in search engines. A great site though will incorporate lots of things that “SEO experts” are chattering about like validating markup, good titles and things like that but that doesn’t mean it is those parts that put it high in the searchresults.

When working on a new version the CMS I use for CrazyBeaver Softwares website I ran into a really disturbing problem with how ASP.Net handles metadata. Look at the sample below.

<head runat="server">
    <title>My website</title>
    <meta name="keywords" content="" />
</head>

It looks alright doesn’t it? Well if you browse to that page you’ll see that the generated source is different.

<head>
    <title>My website</title>
    <meta name="keywords" />
</head>

Where did my content go? Just because it is empty doesn’t mean it can be removed just like that. If you pay a quick visit to www.w3.org and look at the xhtml specification you’ll see that the content attribute is a REQUIRED attribute of the meta element and therefore shouldn’t be removed. Now I hear you say that it shouldn’t matter since it didn’t convey any information anyway and while that is true the removal of that attribute also made sure that the page won’t generate valid html. Sure, it’s a very small error and won’t stop anything that doesn’t require a strict DTD validation to display the page (and no browsers do that since they wouldn’t be able to browse large parts of the web) but start out small and soon you won’t even bother to close tags properly since the page doesn’t validate anyway.

So, this is a behavior of ASP.Net that we need to work around somehow. There are a few choices here. You could simply loop through the HtmlMeta controls during Page_Load and check if they have empty content. It would look something like this.

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        Control[] headerControls = new Control[Page.Header.Controls.Count];
        Page.Header.Controls.CopyTo(headerControls, 0);

        foreach (Control control in headerControls)
        {
            HtmlMeta meta = control as HtmlMeta;

            if (meta == null)
                continue;

            if (string.IsNullOrEmpty(meta.Content))
            {
                Page.Header.Controls.Remove(control);
                break;
            }
        }
    }
}

Does this look as a good enough solution for you? It will do the work needed for most parts (it won’t remove a completely empty meta for some reason though so a <meta name=”" content=”" />will still fail your validation..) but it isn’t the nicest solution since you’ll need to loop through all controls in your header for every pageview that isn’t a postback. Also, maybe you want to specify an empty content for some reason since you have some other piece of code or application that relies on them being there. So what would be a nicer solution? Well writing an adapter of course! In ASP.Net 2.0 they introduced a cool thing called control adapters which basicly let you override the rendering of an existing webcontrol. In this case we will use it to force the HtmlMeta control to always render a content attribute.

using System.Web.UI;
using System.Web.UI.Adapters;
using System.Web.UI.HtmlControls;

namespace CrazyBeavers.Web.UI.Adapters
{
    public class HtmlMetaAdapter : ControlAdapter
    {
        private static string[] _i8nAttributes = new string[] { "lang", "xml:lang", "dir" };

        protected override void Render(HtmlTextWriter writer)
        {
            HtmlMeta meta = this.Control as HtmlMeta;

            writer.WriteBeginTag("meta");

            if (!string.IsNullOrEmpty(meta.HttpEquiv))
                writer.WriteAttribute("http-equiv", meta.HttpEquiv);

            if (!string.IsNullOrEmpty(meta.Scheme))
                writer.WriteAttribute("scheme", meta.Scheme);

            if (!string.IsNullOrEmpty(meta.Name))
                writer.WriteAttribute("name", meta.Name);

            foreach (string attr in meta.Attributes.Keys)
                foreach (string i18n in _i8nAttributes)
                    if (attr.Equals(i18n, System.StringComparison.InvariantCultureIgnoreCase))
                        writer.WriteAttribute(i18n, meta.Attributes[i18n]);

            writer.WriteAttribute("content", meta.Content);

            writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        }
    }
}

Just drop that piece of code in your App_Code or compile it as an assembly and put it in the Bin-folder on your site. There is still one step that needs to be completed for this to work though. All control adapters needs to be referenced via an browser-file in App_Browsers aswell (which is a really cool thing since you can set conditions and use adapters on certain controls for certain browsers, but more on that some other time). Below is a short sample of my Default.browser-file, it doesn’t use any extra features of the browser-file and just say that I want to use my adapter everywhere.

<browsers>
  <browser refID="Default">
    <controladapters>
      <adapter controlType="System.Web.UI.HtmlControls.HtmlMeta"
               adapterType="CrazyBeavers.Web.UI.Adapters.HtmlMetaAdapter" />
    </controladapters>
  </browser>
</browsers>

There you go, now all your MetaCotrol should always render properly for you even though you don’t put any data at all in them. Feel free to comment on this article if you have other solutions or ideas on how to work around this problem.

Recursive generic extensionmethods are fun! (Recursive FindControl)

November 12th, 2008 No comments

Just thought I should share a little piece of code with you this evening. It’s a little extension method (i.e it requires .Net 3.5) that extends Control and helps you find all instances of a certain controltype under the Control on which you invoke the method. Just put the following code in an assembly (or a .cs-file in App_Code).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;

/// <summary>
/// Holderclass for extension methods
/// </summary>
public static class ExtensionMethods
{
    /// <summary>
    /// Recursively searchs for all instances of a specified control type.
    /// </summary>
    /// <typeparam name="T">The type of control to search for.</typeparam>
    /// <param name="control">The control to search from.</param>
    /// <returns>A List<> with the found controls.</returns>
    public static List<T> FindControls<T>(this Control control) where T : Control
    {
        List<T> myList = new List<t>();
        FindControls</t><t>(control, ref myList);
        return myList;
    }

    private static void FindControls<T>(Control control, ref List<T> controlList) where T : Control
    {
        for (int i = 0; i < control.Controls.Count; i++)
        {
            Control currentControl = control.Controls[i];
            if (currentControl is T)
            {
                T typedControl = control.Controls[i] as T;
                controlList.Add(typedControl);
            }

            FindControls<T>(currentControl, ref controlList);
        }

    }
}

To call it you just invoke FindControls<Control>() on any object that derives from Control (i.e. Page, Label, Calendar, the list can go on forever), see the sample below. Hope you find some use for it!

List<HtmlMeta> meta = Page.FindControls<HtmlMeta>();