aspxerrorpath in query string can cause custom error pages to fail with Runtime Error

A bit of a strange and obscure problem here, which is why I am posting about it. I think this may be a problem with ASP.NET rather than the application but I’m willing to be proved wrong!

ASP.NET has a reserved query string variable called aspxerrorpath. This is used by default when a custom error page is invoked, i.e. if you have a 404 or 500 error, and catch it via a custom error page set up in the web.config, then the query string will be something like Error?aspexerrorpath=/Path/To/Location/Error/Occurred. The URL after the equals sign can be used to redirect or log the error page. This happens with MVC as well as Webforms.

However if there is a problem with the aspxerropath URL, for instance it is too long, then the application will fall over with a Runtime Error screen. This is probably because ASP.NET is attempting to parse the URL, it fails, but obviously can’t go back to the custom error page because otherwise it would keep going round in an infinite loop.

Although it is easily possible to remove aspxerrorpath from the error page’s URL, simply by adding an alternative query parameter, the problem is that hacker-types can still manually type it in and possibly gain a little bit more information about your site. It was highlighted as a ‘Low risk’ problem by a security company on a website of mine so I thought I should get it sorted.

I found that following ‘fix’ for another problem seemed to work for this as well, basically it just makes ASP.NET stop parsing the passed URL by instructing it to ignore URLs with aspxerrorpath in the querystring.

http://weblogs.asp.net/scottgu/archive/2010/09/24/update-on-asp-net-vulnerability.aspx

Note I am using .Net 4.0, and the above is an old blog post, so there still seems to be some issues with it. I’m not entirely sure why they can’t remove it altogether!

IE8 clips the text from a max-widthed drop-down box – fix!

OK, Windows XP only has about 6 months to live from the date of this post, and so IE8 (which is XP only) will die out sooner rather than later, but if you are in the unfortunate position of having to try and support ancient browsers then you will have almost certainly come across the problem with fixed-width or max-width drop-down lists. If the width of your options exceeds the width (or max width) of the drop-down box then IE8 just clips the text, which may well mean that your users can’t read it. Another useful feature from IE.

There is a suggested solution here for fixed width drop-downs: http://jquerybyexample.blogspot.com/2012/05/fix-for-ie-select-dropdown-with-fixed.html, which is useful however does not quite address the problem of drop-down boxes with no fixed width defined, but which do have a max-width defined.

The solution is a modification of the above link. Firstly wrap your drop down in a div with a width the same as the max-width of the drop-down, and an overflow value of hidden. The example below shows a Razor ASP.NET drop down, but this will work with HTML too.

  <div style="width:400px;overflow:hidden;">
            @Html.DropDownList("Selection", Model.ChoiceList, new { id = "ddlChoose", @class="ddlClass", style = "max-width:400px; text-overflow: ellipses; " }) 
  </div>

Then in the jQuery $(document).ready() method put the following code on the change, blur, mousedown and mouseup events for your drop-down. In this example I’ve done the element selection by class, but obviously you can use ID if you want. I have used the jQuery ‘live’ method as my drop-downs were not necessarily generated on page load, however you can just use the mousedown() etc methods if you are not doing anything fancy.

$(document).ready(function () {

    /* Begin functions for IE8 drop-down boxes */

    $(".ddlClass").live("mousedown", function () {
        SetToAutoWidth($(this), true, 400);
    });

    $(".ddlClass").live("change", function () {
       ResetToFixedWidth($(this), true, 400);
    });
    $(".ddlClass").live("blur", function () {
       ResetToFixedWidth($(this), true, 400);
    });

    $(".ddlClass").live("mouseup", function () {
       ResetToFixedWidth($(this), true, 400);
   });

  
    /* End functions for IE8 drop-down boxes */

});

This final bit of jQuery contains the methods that actually do the work. Note that you could probably make the FindTextWidth() method more efficient by only running it on page load, and persisting the div (as mentioned in the stackoverflow reply I took this from).

// Return the width (in pixels) of the passed string
// From here: http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript and slightly modified
function FindTextWidth(textToMeasure, fontFamily, fontSize){
    var o = $('<div>' + textToMeasure + '</div>').css({ 'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font-family': fontFamily, 'font-size': fontSize }).appendTo($('body'));
    var w = o.width();

    o.remove();
    return w;
}

// Look through all the options on the drop-down. If any are longer than the passed width value then expand the box.
// Params: 
// ddl - the dropdown list object
// maxWidthFlag - if true then set the max-width, else set the width
// width - the option width value over which the functionality is invoked.
function SetToAutoWidth(ddl, maxWidthFlag, width) {
    if ($.browser.msie && $.browser.version.substring(0, 2) === "8.") { // IE8 only

        var ID = $(ddl).attr("id");
        var ff = $(ddl).css("font-family");
        var fs = $(ddl).css("font-size");

        $("#" + ID + " > option").each(function () { // For each option in the list...

            var t = $(this).text(); // Get the text
            var w = FindTextWidth(t, ff, fs); // Find the width in pixels

            if (w >= width) { // If the width is greater than the passed value
                if (maxWidthFlag === true) {
                    $(this).parent().css("max-width", ""); // Remove the max-width style
                }
                else {
                    $(this).parent().css("width", "auto"); // Set the width style to auto
                }

            }
        });
    }
}

// Params: 
// ddl - the dropdown list object
// maxWidthFlag - if true then set the max-width, else set the width
// width - the option width value over which the functionality is invoked.
function ResetToFixedWidth(ddl, maxWidthFlag, width) {
    if ($.browser.msie && $.browser.version.substring(0, 2) === "8.") { // IE8 only

        if (maxWidthFlag === true){
             $(ddl).css("max-width", width + "px");
        }
        else{
            $(ddl).css("width", width + "px");
        }
    }
}

Note that this still does not get around the problem of the greater-than-the-max-width selected option overflowing the main drop-down box and overwriting the down-arrow. If anyone has a solution for that that doesn’t involve adding an image to it please say!

Executing an ASP.NET HttpModule for specific pages only

By default an ASP.NET HttpModule runs for all webpages on a website. If you use an HttpHandler instead then it is possible to specify the path it executes for in the web.config, but using a handler may not always be possible/desirable in your circumstances.

A module can read the web.config though, so you can create an appSetting entry containing the pages that you want to show, like this:

<appSettings>
    <add key="ModulePages" value="Wibble.aspx,Wobble.aspx"/>
</appSettings>

And then your module sourcecode can be adjusted similar to the following (the key code is in CheckPath()):

   public class HumMasterPage:IHttpModule
    {
        private HttpApplication _current = null;

        #region IHttpModule Members

        public void Init(HttpApplication context)
        {
            _current = context;
            _current.BeginRequest += new EventHandler(context_BeginRequest);
        }

        #endregion


        /// <summary>
        /// DynamicMasterPath appsetting tells us which path the module should run for
        /// </summary>
        /// <returns>true if the current path is in the allowed paths, or allowed paths are not specified (i.e. all are allowed)</returns>
        private bool CheckPath()
        {
            if (ConfigurationManager.AppSettings["ModulePages"] == null)
                return true;

            string[] allowedPaths = ConfigurationManager.AppSettings["ModulePages"].ToString().Split(',');
            
            foreach(string allowedPath in allowedPaths)
            {
                if (_current.Context.Request.Url.PathAndQuery.Contains(allowedPath))
                    return true;
            }

            return false;
        }

        void context_BeginRequest(object sender, EventArgs e)
        {
            if (CheckPath() == false)
                return;
 
            // Do your module stuff here...
        }

    }

You obviously may need to modify it if you have several pages named the same in your site, and I have to admit that I’ve only tested with Webforms, not MVC (I can’t remember off the top of my head if MVC routing is done before or after the module processing). It works well with Webforms however, and there are often better ways to process the stream using MVC anyway.

Internet Explorer does not refresh an ASP.NET MVC Ajax form correctly

I’ve come across a problem with Internet Explorer (I’ve looked at versions 8 and 10) when using a form called via Ajax in MVC. Basically, if you have a standard call to a Ajax.ActionLink such as this:

@Ajax.ActionLink("Edit Thingy", "Edit", "Thingy", new { id = thingy.Code }, new AjaxOptions { UpdateTargetId = "editMyThingy" })
 

Then IE appears to cache your form, so that the next time you view it the values in it are exactly the same as when you first loaded it up, making it look like nothing has been saved (it has been saved, it just looks like it hasn’t to the end user).

The obvious thing to try is to use the OutputCache attribute on your method like so:

[HttpGet]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")] 
public ActionResult Edit(string id)
{
 ...
}
 

However IE seems to ignore this. Eventually I got around the problem in a clunky way by forcing the ActionLink to call the method via a Post rather than a Get (see below). This seems to kick IE back into gear and doesn’t cache. Seems a bit hacky though, so if anyone has any other ideas please let me know!

@Ajax.ActionLink("Edit Thingy", "Edit", "Thingy", new { id = thingy.Code }, new AjaxOptions { UpdateTargetId = "editMyThingy", HttpMethod="Post"  })
 
[HttpPost]
public ActionResult Edit(string id)
{
 ...
}
 

Using MVC’s Html.RenderPartial in a form with the Razor view engine

I’ve found a little quirk with the Razor view engine (MVC3). Had me head-scratching for an hour or so, so I thought I should post it here! The following might only apply inside a partial view with Ajax, I’ve not had a look at non-Ajax or full views yet.

If your view/partial view has an Ajax form, and within that form a call to RenderPartial, like so:

@using (Ajax.BeginForm("MyAction", "MyController", new AjaxOptions() { /* various form options */}))
{
    <div>
       Some stuff
    </div>

    @{ 
         Html.RenderPartial("My Partial", Model); 
     }
}

Then although this looks fine you will get the following error message: CS1501: No overload for method ‘Write’ takes 0 arguments

To get it working simply surround the call to RenderPartial with a div tag, like below:

@using (Ajax.BeginForm("MyAction", "MyController", new AjaxOptions() { /* various form options */}))
{
    <div>
       Some stuff
    </div>

    <div> 
    @{ 
         Html.RenderPartial("My Partial", Model); 
     }
    </div>
}

Why we have more than one unique identifier in a table with Sharp Architecture

I was failing to explain to non-OO-aware colleagues about why Sharp Architecture pushes you towards having two unique identifiers on a database table, so I went away, sorted out how to explain Domain Signatures simply in my head, and came up with this explanation. It is hopefully clear, and hopefully correct, so I’ve posted it here to refer back to in future!

There are two reasons:

  1. Ideological: Sharp Architecture is about Domain Driven Design (DDD). A part of DDD is called Persistence Ignorance, the idea is that in order to be usable an entity should not need its database identifier, i.e. you need some way of identifying an object which would work if you were connected to a database or not. This identifier doesn’t need to be another ID code, but you must have a way of uniquely identifying an object (hence the DomainSignature attribute on the property). This allows much easier communication to outside systems too, if necessary, as all systems will know what they are talking about. This article explains it reasonably clearly: http://msdn.microsoft.com/en-us/magazine/dd882510.aspx.
  2. Practical: You cannot easily unit test a method that includes a search for a Sharp Architecture entity that uses Id as the only unique identifier. The Id property is read-only, so you can’t set it in your unit test’s mini ‘database’.

Unit testing MVC’s Authorize attribute with Rhino Mocks

If there is one thing I often forget to do at initial development time, it is to include an Authorize attribute on my controllers to allow only certain specified roles have access. So I’ve added a check to my ‘template’ of controller tests now.

If you have a controller declaration similar to this:

[Authorize(Roles = "Admin")]
public class MyController : Controller
{
    ...
}

Then you can test for the presence of the Authorize attribute in your test class like so (include a reference to System.Web.Mvc):

[Test]
public void Check_Auth_Privileges_Are_Correct()
{

     AuthorizeAttribute attribute = typeof(MyController)
            .GetCustomAttributes(typeof(AuthorizeAttribute), true)
            .Cast<AuthorizeAttribute>()
            .FirstOrDefault();

     Assert.NotNull(attribute); // Check the attribute exists
     Assert.That(attribute.Roles.Contains("Admin"));  // Check it contains your role
     // Check that it doesn't contain any other roles (if necessary)
}

If you have just declared the attribute on a particular method rather than the whole controller then you can test for its presence by just adding an extra line:

[Test]
public void Check_Auth_Privileges_Are_Correct()
{
     var methodInfo = typeof(MyController).GetMethod("MyMethod");
     AuthorizeAttribute attribute = methodInfo
            .GetCustomAttributes(typeof(AuthorizeAttribute), true)
            .Cast<AuthorizeAttribute>()
            .FirstOrDefault();

     Assert.NotNull(attribute); // Check the attribute exists
     Assert.That(attribute.Roles.Contains("Admin"));  // Check it contains your role
     // Check that it doesn't contain any other roles (if necessary)
}

You can obviously use this method for other annotations too.