Using an HttpModule to Redirect a Request When an Uploaded File is Too Big

NB: an improved version of this module is here.

Today I wanted to build something into an ASP.NET MVC application to impose a size limit on uploaded files. I wanted to:

  1. Use an HttpModule so I could get at the upload before any Controllers got involved, and
  2. Redirect the request if the file was too big

I did this without much fuss, but the redirect ended up at an ASP.NET "Maximum request length exceeded" error page. I found this solution using custom error pages on Stackoverflow, and this one reading the whole request and then redirecting on VelocityReviews, but I wanted to have the entire solution in one class, and I just wanted to do a redirect, darnit.

So I came up with this; it's an HttpModule which throws an InvalidOperationException with a unique error message if the request is too big, then catches that same error, calls Server.ClearError(), and performs the redirect. The original request has "/FileTooBig" appended to it so the page knows to display a error message about file upload size.

using System;
using System.Configuration;
using System.Web;

public class UploadedFileSizeScreeningModule : IHttpModule
{
    private static readonly string _uploadTooBigErrorMessage = Guid.NewGuid().ToString();

    public void Init(HttpApplication application)
    {
        application.BeginRequest += ValidateUploadRequest;
        application.Error += HandleFileUploadError;
    }

    public void Dispose()
    {
    }

    private static void ValidateUploadRequest(object source, EventArgs e)
    {
        HttpApplication context = source as HttpApplication;

        int maxFileSizeInMB;
        if (!TryGetMaxAllowedFileSize(out maxFileSizeInMB))
        {
            return;
        }

        if (!IsValidFileUploadRequest(context))
        {
            return;
        }

        if (!IsUploadedFileSmallEnough(context.Request.ContentLength, maxFileSizeInMB))
        {
            throw new InvalidOperationException(_uploadTooBigErrorMessage);
        }
    }

    private static bool TryGetMaxAllowedFileSize(out int maxFileSizeInMB)
    {
        string maxFileSizeSetting = ConfigurationManager.AppSettings["MaxFileUploadSize"];

        if (string.IsNullOrWhiteSpace(maxFileSizeSetting))
        {
            // If the config setting hasn't been specified,
            // default to 5MB:
            maxFileSizeInMB = 5;
            return false;
        }

        return int.TryParse(maxFileSizeSetting, out maxFileSizeInMB);
    }

    private static bool IsValidFileUploadRequest(HttpApplication context)
    {
        if (context.Request.HttpMethod.ToUpperInvariant() != "POST")
        {
            return false;
        }

        // Do something more sensible / appropriate here to check
        // if the request requires upload size validation:
        return context.Request.Path.Contains("FileUploadPathIdentifier");
    }

    private static bool IsUploadedFileSmallEnough(int requestLengthInBytes, int maxFileSizeInMB)
    {
        // Divide by 1024 twice - once to get to KB, once to get to MB:
        int requestLengthInMB = requestLengthInBytes / 1024 / 1024;

        return requestLengthInMB <= maxFileSizeInMB;
    }

    private static void HandleFileUploadError(object sender, EventArgs e)
    {
        HttpApplication context = sender as HttpApplication;

        if (context == null)
        {
            return;
        }

        Exception thrownException = context.Server.GetLastError();

        if (thrownException.Message == _uploadTooBigErrorMessage)
        {
            context.Server.ClearError();

            string newPath = string.Concat(context.Request.Path, "/FileTooBig");

            context.Response.Redirect(newPath, true);
        }
    }
}

Print | posted @ Wednesday, April 6, 2011 9:59 PM

Comments on this entry:

Gravatar # re: Using an HttpModule to Redirect a Request When an Uploaded File is Too Big
by AndyB at 8/9/2011 11:41 AM

Steve

I tried following what you had implemented here, and everything is fine until the last line " context.Response.Redirect(newPath, true);
" on which the unhandled error for maxium request length is actually thrown and unhandled. This means I am back to square one. Any thoughts?
Gravatar # re: Using an HttpModule to Redirect a Request When an Uploaded File is Too Big
by Steve at 8/9/2011 1:38 PM

Hi Andy,

It sounds like what you've run into a file larger than the request length configured in IIS, as detailed here: http://support.microsoft.com/default.aspx?scid=kb;EN-US;295626.

This HttpModule copes with files which are below this value but larger than a "MaxFileUploadSize" value you have in your appSettings; depending on the size of the file you're uploading you should be able to get around it by changing the config values detailed in the knowledge base article.
Gravatar # re: Using an HttpModule to Redirect a Request When an Uploaded File is Too Big
by AndyB at 8/10/2011 1:22 PM

thanks for the prompt response Steve.
I did wonder about your custom appSetting "MaxFileUploadSize". So basically, you are circumventing the max file size by maxing the maxRequestLength and then checking against your own custom setting and dealing with your own exception as apposed to the HttpException?

I guess this is one way round this, but I just thought by now there would be a better way of dealing with it(i.e. it would be a solved problem). I have seen the aysnc file upload control, so maybe that is Microsoft's long term solution, I don't know.
Gravatar # re: Using an HttpModule to Redirect a Request When an Uploaded File is Too Big
by Steve at 8/10/2011 4:27 PM

Hi Andy,

I've just figured out a way around this second type of error, so I'll post another blog with that in over the coming days :)
Post A Comment
Title:
Name:
Email:
Comment:
Verification: