CodeProject, Software Development

Injecting a Dependency into an IHttpModule with Unity

We’re starting a new project, and as part of that we want to get better at certain things. One is unit testing the things we didn’t last time around that were in hard-to-reach places. Pretty much most things that interact with ASP.NET have hard-to-reach places. Even ASP.NET MVC, which was supposed to be wonderful and much more unit testable that vanilla ASP.NET, has lots of places where this falls down completely. However, we’re gradually finding way to overcome these obstacles.

In this post, I’m going to concentrate on custom IHttpModule implementations.

We have a custom IHttpModule that requires the services of another class. It is already set up in our IoC container and we just want to inject it into the module we’re writing. However, modules are instantiated by ASP.NET before our IoC framework can get to it.

How I got around this was by creating an additional module (an InjectorModule) that wired up all the other modules that needed dependencies injected using Unity’s BuildUp method to inject the dependency into the existing object.

Setting up the HttpApplication

The application object stores the container and implements an interface that the InjectorModule can access the container through.

public interface IHttpUnityApplication
{
    IUnityContainer UnityContainer { get; } 
}

And the Application class in the global.asax.cs file looks like this:

public class MvcApplication : System.Web.HttpApplication, IHttpUnityApplication
{
    // This is static because it is normal for ASP.NET to create
    // several HttpApplication objects (pooling) but only the first
    // will run Application_Start(), which is where this is set.
    private static IUnityContainer _unityContainer;

    protected void Application_Start()
    {
        _unityContainer = UnityBootstrapper.Initialise();
	// Do other initialisation stuff here
    }

    // This implements the IHttpUnityApplication interface
    public IUnityContainer UnityContainer
    {
        get { return _unityContainer; }
    }
}

The UnityBootstrapper initialises the container for MVC, it is created by the Unity.Mvc4 NuGet package (there’s also a Unity.Mvc3 package too). You can read more about it here.

The InjectorModule

Next up the InjectorModule is created

public class InjectorModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        // Get the IoC container from the application class through
        // the common interace.
        var app = (IHttpUnityApplication) context;
        IUnityContainer container = app.UnityContainer;

        // Wire up each module that is registered with the IoC container
        foreach (var module in context.GetRegisteredModules(container))
            container.BuildUp(module.GetType(), module);
    }

    public void Dispose()
    {
    }
}

I’ve also been a wee bit sneaky and created an extension method on HttpApplication to work out which are the registered modules so that the code above is a bit nicer. That code is:

public static class HttpApplicationExtensions
{
    public static IEnumerable GetRegisteredModules(this HttpApplication context, IUnityContainer container)
    {
        var allModules = context.Modules.AllKeys.Select(k => context.Modules[k]);
        var registeredModules = allModules.Where(m => container.IsRegistered(m.GetType()));
        return registeredModules;
    }
}

Wiring it all up

The container must be told which modules have dependencies to inject and what properties to set. e.g.

container.RegisterType<MyCustomModule>(
    new InjectionProperty("TheDependentProperty"));

MyCustomModule is the class that implements the IHttpModule interface, and you need to supply an InjectionProperty for each of the properties through which the IoC containers will inject a dependency.

You can also decorate the properies with the [Dependency] attribute, but then you are just wiring in a dependency on the IoC container itself… which is not good.

Finally, this new module has to be wired up in the web.config file

  <system.webServer>
    <modules>
      <add name="InjectorModule"
           type="Web.IoC.Unity.InjectorModule" />
      <!-- Other modules go here, after the Injector Module -->
    </modules>

By putting the Injector module ahead of other modules in the web.config it means it gets a chance to run and inject the depedencies into other modules that have yet to be initialised.

Other considerations

The IHttpModule interface defines a method, Init(), that takes an HttpApplication as a parameter. Naturally, that’s difficult to mock out in a unit test.

What I did was to extract all the bits that I needed in the Init() method and pass them to another method to do the work. For example, HttpContext is easy to do because ASP.NET MVC provides an HttpContextWrapper and the method that is doing all the work just takes an HttpContextBase, which is easily mocked in a unit test.

public void Init(HttpApplication context)
{
   var wrapper = new HttpContextWrapper(context.Context);
   InitImpl(wrapper);
}
public void InitImpl(HttpContextBase httpContext)
{
    // Do stuff with the HttpContext via the abstract base class
}
CodeProject, Software Development

Automatically replacing an image on an HTML page when it is not found.

The project I’m working on has just moved the image hosting to Amazon S3. Previously what happened was that we had a big folder full of images that had been uploaded from our users and that if the site needed to render an image it would check the directory for the image it needed, if it didn’t have it, it would look for the original and then resize and render that (storing the resized version in the folder so it can be found the next time). If the original couldn’t be found either it displayed a replacement image in place that was basically an image that said “There is no image available.”

That worked well enough with a small number of users but it really didn’t scale well.

Now that we’ve moved the hosting to Amazon S3 we create all the image sizes needed at the time they are initially uploaded. If we need a new size we have a tool that will go and create all the resized versions for us. The only issue that remains is that some images don’t exist for various reasons. Much of the legacy data came from systems that were installed on people’s desktops and the image data simply never got sync’ed to the central server properly.

But there is a way around this on the browser. The img tag can have an onerror attribute applied, which can then call a function which replaces the image src with a dummy image that contains the message for when there is no image.

For example:

<div>
  <img src="error.jpg" onerror="replaceImage(this, 'replacement.jpg');" title="This image is replaced on an error"/>
</div>

<script type="text/javascript">
  function replaceImage(image, replacementUrl){
    image.removeAttribute("onerror");
    image.src=replacementUrl;
  }
</script>

Although this looks a little ugly (putting in lots of onerror attributes on images) there is a lot less code to be written. When trying to achieve the same results in jQuery I eventually gave up. That’s not to say that it can’t be done, just that for pragmatic reasons I didn’t pursue it as I was spending too much time trying to get it to work.

The function does two things, first it removes the onerror because if the replacementUrl is also broken it will just recurse the call to the error handler and the browser will just slow right down. Second, it performs the actual replacement.

To see it in action there is an example page to demonstrate it.

I also tried to create a jQuery based solution to fit in with everything else. However, there were a couple of problems with a jQuery solution that were less than ideal.

  • You can’t attach an error event to the images because by the time you have done so the error event will be long past. You have to loop around all the images initially to find out which didn’t load before jQuery got a chance to get going.
  • For images that are added to the page by jQuery itself the .on does not work because delegated events, which allow you to create event handlers on elements before they are created, need the events to bubble up to a parent that did exist at the point the event handler was attached. The error event, among a small set of other events, does not bubble up. And if you attach it directly to the newly created element on the page then it will likely be too late, especially on a fast connection, as it will have already fired off the error event. You could do the same as before and check manually to see if the image loaded or not – but then the code is getting rather unwieldy and unmanageable.

In the end I found that the small bit of code that is called from the onerror attribute on each img element that needed it was more compact and didn’t require lots of extra lines of code to ensure that all the errors were corrected in the case that jQuery just didn’t get there in time.

Finally, if anyone has a solution in jQuery that does not require cluttering up the HTML, I’d like to see it

CodeProject, Software Development

Quick guide to Geolocation in Javascript

In some modern browsers, such as Chrome and Firefox you can access the geolocation of the device. That is, where the device is physically located.

The main function for achieving this is getCurrentPosition, which doesn’t return a position as you might expect. Rather, it takes a callback (and optionally a second if you want to handle error conditions).

I’ve put together a small example page showing this, which I’ll now walk through.

In the example, when the user clicks on the button on the page it will attempt to get the physical location of the device. This may or may not work for several reasons. If it doesn’t work then the browser may not support it, or the user may refuse to give permission to the site, or the geolocation service may not be working.

This first bit of code checks to see if the browser supports the geolocation API and if it does calls the function to get the location passing in the callbacks for success and error handling.

if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
} else {
    displayErrorMessage("The browser does not support the Geolocation API.");
}

In this small example, the successCallback simply fills various spans with the results in the position and creates a URL that links to google maps to display a pin at the coordinates.

function successCallback(position) {
    $("#latitude").html(position.coords.latitude);
    $("#longitude").html(position.coords.longitude);
    $("#accuracy").html(position.coords.accuracy);
    $("#displayMap").attr("href", "http://maps.google.com/?q=" + position.coords.latitude + "," + position.coords.longitude);
    $("#displayMap").removeClass("disabled");
}

The position has a timestamp and a set of coordinates. Since the geolocation may be cached the timestamp will give you an indication of how old the geolocation is.

The coords gives you various bits of information about the geolocation. The three values that will always be available are latitude, longitude and accuracy. The other values (such as altitude, heading and speed) may be nullable. The accuracy is in meters and can be used to gauge how good the lat/long is. The Lat/Long is in WGS84 decimal degrees.

In the event of an error, the errorCallback will receive some indication about what went wrong. The most common may be that the permission was denied, but other potential errors exist.

function errorCallback(error) {
    switch (error.code) {
        case error.PERMISSION_DENIED:
            displayErrorMessage("The request was denied. If a message seeking persmission was not displayed then check your browser settings.");
            break;
        case error.POSITION_UNAVAILABLE:
            displayErrorMessage("The position of the device could not be determined. For instance, one or more of the location providers used in the location acquisition process reported an internal error that caused the process to fail entirely.");
            break;
        case error.TIMEOUT:
            displayErrorMessage("The request to get user location timed out before the operation could complete.");
            break;
        case error.UNKNOWN_ERROR:
            displayErrorMessage("Something unexpected happened.");
            break;
    }
}

 

How your browser reacts to requests for geolocation

Your browser may give you some form of alert to indicate that the site is requesting the geolocation. Chrome, for example, displays a bar just under the omnibox

Chome asks if it is okay to use geolocation

If a site has permission to get the geolocation then the icon above will be displayed in the omnibox to the right of the URL. If not, the icon will have a red cross over it. You can click this icon to change the settings at any time.

 

Finally, if you want to read the spec in full, it is available here: http://www.w3.org/TR/geolocation-API/