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.

Advertisements

web.config transforms for CAS integration

I have previously posted about using a different web.config file for each build configuration, to save you having to change your web.config each time you want to change from a development build to a production build etc (Read here).

That was some time ago though and MVC3 has since moved on to use web.config transforms, where you use XPath style commands to change the web.config file for each configuration.

As a useful reference here are the transforms I used for adding CAS integration into my app. You only need to change the attributes of the authentication node if you are not using forms authentication as your default. If you are using forms authentications as your default then you will probably need to use xdt:Transform=”RemoveAll” with the forms nodes, before you add the new one too.

More about web.config transforms is here.

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">

  <configSections>
    <section name="casClientConfig"
           type="DotNetCasClient.Configuration.CasClientConfiguration, DotNetCasClient" xdt:Transform="Insert"  />
  </configSections>

  <casClientConfig
  casServerLoginUrl="https://login.casserver.com/cas/login"
  casServerUrlPrefix="https://login.casserver.com/cas/"
  serverName="http://myservername.com"
  notAuthorizedUrl="~/Security/NotAuthorised"
  cookiesRequiredUrl="~/Security/CookiesRequired"
  redirectAfterValidation="true"
  renew="false"
  singleSignOut="true"
  ticketValidatorName="Cas10"
  serviceTicketManager="CacheServiceTicketManager" xdt:Transform="InsertAfter(/configuration/configSections)" />


  <system.web>

    <authentication
       
        mode="Forms"
        xdt:Transform="SetAttributes">

      <forms
       loginUrl="https://login.casserver.com/cas/login"
       timeout="30"
       defaultUrl="/"
       cookieless="UseCookies"
       slidingExpiration="true"
       path="/MyVirtualPath" xdt:Transform="Insert" />
      
    </authentication>

    <httpModules>
      <add name="DotNetCasClient"
           type="DotNetCasClient.CasAuthenticationModule,DotNetCasClient" xdt:Transform="Insert" />
    </httpModules>
    
 
 
  </system.web>

  <system.webServer>
    <modules>
      <remove name="DotNetCasClient" xdt:Transform="Insert"/>

      <add name="DotNetCasClient"
           type="DotNetCasClient.CasAuthenticationModule,DotNetCasClient" xdt:Transform="Insert"/>
    </modules>
    
  </system.webServer>
</configuration>

Using web.config application settings in nUnit

If the piece of code that you are testing refers to an appsetting value in the web.config file then nUnit will fall over – basically because nUnit itself does not use the web.config file.

This is a fairly common thing to come up against, and the easy solution is on the web, but it doesn’t appear to be in the nUnit help so I thought I’d put it here too, and make it specific to the dependency injection/Sharp Architecture web projects that my team use.

There are two things that you can do. The quick thing to do is to find your tests dll file. In a Sharp Architecture 1.6 project this will be in something like [project root]/tests/[namespace].Tests/bin/Debug. The dll file will be called (unsurprisingly) [namespace].Tests.dll.

You will see that nUnit has also created a [namespace].Tests.dll.config file. This is where you should put your application settings. When you open it up you can see that it looks similar to the web.config file – there is already an appsettings section. Simply copy your web.config settings to here.

That is all very well, but what if you want to mock different values of this app setting when you are testing? If you are using a dependency injection development pattern you might also want to consider having a separate class and interface that just handles the retrieval of application settings, e.g.

public class AppSettingsService: IAppSettingsService
{
        public string ManagerRole
        {
            get { 
              if (ConfigurationManager.ConnectionStrings["ManagerRole"] == null)
                 return "";
              else
                 return ConfigurationManager.ConnectionStrings["ManagerRole"].ToString();  
            } 
        }
}

You can then inject this service class into the class that you are testing, use the methods on this service class to retrieve your appsettings, and mock them to your heart’s content.

Note: I am assuming you know what dependency injection, mocking, interfaces etc are. If not I suggest searching the net – they are a very good way of developing!

Using different web.config/NHibernate.config settings for each Visual Studio release configuration

When you have a development, test and production environment it is likely that each one has different connection strings and other application settings. To avoid the tedium (and any errors) of having to manually change the config files each time you change configuration then I suggest Scott Hanselmann’s method of using a pre-build command that chooses one out of multiple config files (one for each configuration) is a less error-prone way of doing it.

His article is here.

There are several other things to note here:

  1. The pre-build command is not quite correct (for my system anyway). Instead of:
    "$(ProjectDir)copyifnewer.bat" "$(ProjectDir)Web.config.$(ConfigurationName)" "$(ProjectDir)Web.config"
    

    You will need:

    call "$(ProjectDir)copyifnewer.bat" "$(ProjectDir)Web.config.$(ConfigurationName)" "$(ProjectDir)Web.config"
    
  2. If you are using NHibernate then you will probably also want to change the NHibernate.config file between releases. In which case simply add another line to the pre-build event:
    call "$(ProjectDir)copyifnewer.bat" "$(ProjectDir)Web.config.$(ConfigurationName)" "$(ProjectDir)Web.config"
    call "$(ProjectDir)copyifnewer.bat" "$(ProjectDir)NHibernate.config.$(ConfigurationName)" "$(ProjectDir)NHibernate.config"
    
  3. If you are using SourceSafe then you will need to make sure that your Web.config and NHibernate.config files are checked out prior to building, otherwise they are read-only and the copy process won’t be able to overwrite them (and you probably wouldn’t want it to anyway).
  4. Of course any new application settings etc probably have to be added into all versions of the config files, rather than just the one.

This method saves a lot of mistakes being made in more complex web.config files.