35

I upgraded my site to use ASP.Net MVC from traditional ASP.Net webforms. I'm using the MVC routing to redirect requests for old .aspx pages to their new Controller/Action equivalent:

        routes.MapRoute(
            "OldPage",
            "oldpage.aspx",
            new { controller = "NewController", action = "NewAction", id = "" }
        );

This is working great for pages because they map directly to a controller and action. However, my problem is requests for images - I'm not sure how to redirect those incoming requests.

I need to redirect incoming requests for http://www.domain.com/graphics/image.png to http://www.domain.com/content/images/image.png.

What is the correct syntax when using the .MapRoute() method?

womp
  • 115,835
  • 26
  • 236
  • 269
The Matt
  • 6,618
  • 7
  • 44
  • 61

2 Answers2

52

You can't do this "out of the box" with the MVC framework. Remember that there is a difference between Routing and URL-rewriting. Routing is mapping every request to a resource, and the expected resource is a piece of code.

However - the flexibility of the MVC framework allows you to do this with no real problem. By default, when you call routes.MapRoute(), it's handling the request with an instance of MvcRouteHandler(). You can build a custom handler to handle your image urls.

  1. Create a class, maybe called ImageRouteHandler, that implements IRouteHandler.

  2. Add the mapping to your app like this:

    routes.Add("ImagesRoute", new Route("graphics/{filename}",
    new ImageRouteHandler()));

  3. That's it.

Here's what your IRouteHandler class looks like:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Compilation;
using System.Web.Routing;
using System.Web.UI;

namespace MvcApplication1
{
    public class ImageRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            string filename = requestContext.RouteData.Values["filename"] as string;

            if (string.IsNullOrEmpty(filename))
            {
                // return a 404 HttpHandler here
            }
            else
            {
                requestContext.HttpContext.Response.Clear();
                requestContext.HttpContext.Response.ContentType = GetContentType(requestContext.HttpContext.Request.Url.ToString());

                // find physical path to image here.  
                string filepath = requestContext.HttpContext.Server.MapPath("~/test.jpg");

                requestContext.HttpContext.Response.WriteFile(filepath);
                requestContext.HttpContext.Response.End();

            }
            return null;
        }

        private static string GetContentType(String path)
        {
            switch (Path.GetExtension(path))
            {
                case ".bmp": return "Image/bmp";
                case ".gif": return "Image/gif";
                case ".jpg": return "Image/jpeg";
                case ".png": return "Image/png";
                default: break;
            }
            return "";
        }
    }
}
Amitd
  • 4,769
  • 8
  • 56
  • 82
womp
  • 115,835
  • 26
  • 236
  • 269
  • Can this work without Response.End method? Just think that throw an exception (from Response.End) on every image request is not the best way... – Kamarey Jul 20 '09 at 16:23
  • That's a good point Kamarey. Rick Strahl has a blog post about that, and the conclusion is that there really isn't a perfect way of handling it. Check here: http://www.west-wind.com/WebLog/posts/368975.aspx – womp Jul 20 '09 at 16:37
  • I read your article... good job. It works great and it is really easy to set up less that 15 minutes – GibboK Aug 16 '11 at 09:57
  • +1 from me - worked without very many tweaks and helped me round an issue I thought might take days...in just an hour. – SpaceBison Jan 12 '12 at 13:38
  • @womp - I am newbie to MVC, just wanted to clarify one thing, can't we ignore the .png files using IgnoreRoute(), so that the request is handled by ASP.NET routing engine, instead of handling the request and then routing to the files in the physical location. – Bibhu Nov 23 '12 at 06:20
  • This didn't work for me with MVC 5 until i added a UrlRoutingHandler as seen here: http://stackoverflow.com/a/13082446/140449. It seems MVC didn't like the period in the filename until this UrlRoutingHandler was added. – jaminto Sep 17 '14 at 20:09
6

If you were to do this using ASP.NET 3.5 Sp1 WebForms you would have to create a seperate ImageHTTPHandler that implements IHttpHandler to handle the response. Essentially all you would have to do is put the code that is currently in the GetHttpHandler method into your ImageHttpHandler's ProcessRequest method. I also would move the GetContentType method into the ImageHTTPHandler class. Also add a variable to hold the name of the file.

Then your ImageRouteHanlder class looks like:

public class ImageRouteHandler:IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            string filename = requestContext.RouteData.Values["filename"] as string;

            return new ImageHttpHandler(filename);

        }
    } 

and you ImageHttpHandler class would look like:

 public class ImageHttpHandler:IHttpHandler
    {
        private string _fileName;

        public ImageHttpHandler(string filename)
        {
            _fileName = filename;
        }

        #region IHttpHandler Members

        public bool IsReusable
        {
            get { throw new NotImplementedException(); }
        }

        public void ProcessRequest(HttpContext context)
        {
            if (string.IsNullOrEmpty(_fileName))
            {
                context.Response.Clear();
                context.Response.StatusCode = 404;
                context.Response.End();
            }
            else
            {
                context.Response.Clear();
                context.Response.ContentType = GetContentType(context.Request.Url.ToString());

                // find physical path to image here.  
                string filepath = context.Server.MapPath("~/images/" + _fileName);

                context.Response.WriteFile(filepath);
                context.Response.End();
            }

        }

        private static string GetContentType(String path)
        {
            switch (Path.GetExtension(path))
            {
                case ".bmp": return "Image/bmp";
                case ".gif": return "Image/gif";
                case ".jpg": return "Image/jpeg";
                case ".png": return "Image/png";
                default: break;
            }
            return "";
        }

        #endregion
    }
awright18
  • 2,255
  • 2
  • 23
  • 22