0

I'm managing an MVC3 app where I need to support the ability of 3rd parties to create link to assets within my domain. Because some of the links are sliced and diced by mail merges and other text editing problems, URLs with typos have been introduced, e.g.:

/Content/ima!+ges/email/spacer.gif

or

/Content/image++s/email+/spacer.gif

I'd like to strip these extraneous characters by RegEx before attempting to serve them. I _think this is something a Route method could accomplish and I'd welcome a pointer or two to articles that demonstrate this approach.

ADDENDUM (cuz I need the formatting):

Implementing @Nathan's routing I'm unable to send the filename to the controller handler - it's always seeing a null value passed in. I've tried both 'filepath' and 'path' with the same 'null' result.

        routes.MapRoute(
          "MangledFilename",
          "{*filepath}",
          new { controller = "MangledFilename", action = "ServeFile" }
          );

I think this is a matter of configuring wildcard handling on IISExpress and am looking for that solution separately. The more serious immediate problem is how your suggestion returns the HttpNotFound - i'm getting a hard IIS exception (execution halts with a YellowScreenDeath) instead of the silent 404 result.

    public ActionResult ServeFile(string filePath)
    {
        if (filePath != null) // workaround the null
        {
               ...
        }
        return HttpNotFound();
    }

thx

justSteve
  • 5,444
  • 19
  • 72
  • 137

2 Answers2

0

I think something along this approach should work:

First add a route like this to the end of your route registering declarations:

    routes.MapRoute(
      "MangledFilename",
      "{*filepath}",
      new { controller = "MangledFilename", action = "ServeFile" });

If you haven't seen them before, a route parameter with an * after the opening { is a wildcard parameter, in this case it will match the entire path. You could also write it like content/{*filepath} if you wanted to restrict this behavior to your content directory.

And then a controller something like this should do the trick:

    public class MangledFilenameController : Controller
    {
        public ActionResult ServeFile(string filePath)
        {
            filePath = CleanFilePath(filePath);
            var absolutePath = Server.MapPath(filePath);

            if (System.IO.File.Exists(absolutePath))
            {
                var extension = System.IO.Path.GetExtension(absolutePath);
                var contentType = GetContentTypeForExtenion(extension);
                return File(absolutePath, contentType);
            }

            return HttpNotFound();
        }

        private string CleanFilePath(string filepath)
        {
            //clean the path up
            return filepath;
        }

        private string GetContentTypeForExtenion(string extension)
        {
            //you will want code here to map extensions to content types
            return "image/gif";
        }
    }

In regards to mapping an extension to a MIME / content type for the GetContentTypeForExtension method, you could choose to hard code types you are expecting to serve, or use one of the solutions detailed in this post:

File extensions and MIME Types in .NET

EDIT:

After thinking about it, I realized there's another way you can handle the ServeFile action. Redirecting to the existing file could be simpler. I'm leaving the original method I wrote above and adding the alternative one here:

    public ActionResult ServeFile(string filePath)
    {
        filePath = CleanFilePath(filePath);
        var absolutePath = Server.MapPath(filePath);

        if (System.IO.File.Exists(absolutePath))
        {
            return RedirectPermanent(filePath);
        }

        return HttpNotFound();
    }
Community
  • 1
  • 1
Nathan Anderson
  • 6,768
  • 26
  • 29
  • Thanks Nathan - I think there's a problem with the bit of code where you 'return HttpNotFound();' - details in my edited question. - appreciated – justSteve Feb 27 '12 at 15:46
0

I believe @Nathan Anderson provided a good answer but it seems incomplete.

If you want to correct the typos and the types are as simple as those you mentioned then you can use Nathan code but before trying to find the file, you remove any plus or exclamation point characters in the path and you can do it like this:

String sourcestring = "source string to match with pattern";
String matchpattern = @"[+!]";
String replacementpattern = @"";
Console.WriteLine(Regex.Replace(sourcestring,matchpattern,replacementpattern));

Generated this code from the My Regex Tester tool.

This is the code you need. This code also removes any + character from the filename. If you don't want that behavior, you may select a substring without the filename and only replace + and ! characters before the filename.

Fabio Milheiro
  • 8,100
  • 17
  • 57
  • 96