Stefan Steiger
Stefan Steiger

Reputation: 82156

MVC URL routing, routes to wrong route, why?

Question:

This is my RegisterRoutes:

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");


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


            // insert_dateti
            routes.MapRoute(
                "Default", // Routenname
                "{controller}/{action}/{id}", // URL mit Parametern
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameterstandardwerte
                //new { controller = "Home", action = "Splash", id = UrlParameter.Optional } // Parameterstandardwerte
                //new { controller = "Folders", action = "Content", id = UrlParameter.Optional } // Parameterstandardwerte
            );



        } // End Sub RegisterRoutes

And this is my route handler

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


namespace HttpRuntime.Routing
{ 


    public class ImageRouteHandler : IRouteHandler 
    {


        public string MapRemoteLocation(string strPath)
        {
            if (string.IsNullOrEmpty(strPath))
                return "";

            string strBaseURL = "http://madskristensen.net/themes/standard/";
            return strBaseURL + strPath;
        }

        public IHttpHandler GetHttpHandler(RequestContext requestContext) 
        { 


            string filename = requestContext.RouteData.Values["filename"] as string; 

            if (string.IsNullOrEmpty(filename)) 
            {
                // return a 404 HttpHandler here
                requestContext.HttpContext.Response.StatusCode = 404;
                requestContext.HttpContext.Response.End();
                return null;
            } // End if (string.IsNullOrEmpty(filename)) 
            else 
            { 
                requestContext.HttpContext.Response.Clear();
                requestContext.HttpContext.Response.ContentType = GetContentType(filename);
                requestContext.HttpContext.Response.Redirect(MapRemoteLocation(filename));


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


                // string stPath = requestContext.HttpContext.Request.Url.AbsolutePath;
                //requestContext.HttpContext.Response.WriteFile(filepath); 
                //requestContext.HttpContext.Response.End();
            } // End Else of if (string.IsNullOrEmpty(filename)) 

            return null; 
        } // End Function GetHttpHandler


        public static void MsgBox(object obj)
        {
            if (obj != null)
                System.Windows.Forms.MessageBox.Show(obj.ToString());
            else
                System.Windows.Forms.MessageBox.Show("obj is NULL");
        } // End Sub MsgBox


        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;
            } // End switch (Path.GetExtension(path))  
            return "";
        } // End Function GetContentType


    } // End Class ImageRouteHandler : IRouteHandler 


}  // End Namespace HttpRuntime.Routing

The goal of this is, that when i have this in /Home/Index.cshtml

<img src="graphics/mads2011.png?v=123" title="Compose E-Mail" alt="Compose E-Mail" />

it downloads the picture from a remote host.

It works fine as long as I have

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

But when I change it to

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

in order to allow subfolders, then it redirects every url action to /graphics.

e.g. when I have

$(function () {
        $("#divJsTreeDemo").jstree({ 
        "json_data" : {
            "ajax" : {
                 "url": "@Url.Action("GetNodesJson", "Home")"
                 //"url": "@Url.Action("GetTreeData", "Home")"
                ,"async" : true

in Home/Index.cshtml

the URL for the AJAX call on the resulting HTML page becomes

"url": "/graphics?action=GetNodesJson&amp;controller=Home"

Why is this ? And how can I fix this ? If I move my ImagesRoute to the bottom, the JavaScript routes correctly, but then I can get no more remote images, because they are routed to controller "graphics", which doesn't exists --> Exception no such view...

Upvotes: 1

Views: 3297

Answers (1)

counsellorben
counsellorben

Reputation: 10924

When building links from routes, your ImagesRoute using {*filename} will satisfy all conditions, so that route will always be used to build links if it precedes your Default route in your routes table. However, when processing requests, it works as expected, because the ImagesRoute route will only be satisfied by requests starting http://mysite.com/graphics/...

In order to fix this, you need to move the ImagesRoutes below the default route, and you then need to add a RouteConstraint to the Default route, so that any request starting with graphics will fail the Default route.

Change your Default route as follows:

routes.MapRoute(
    "Default", // Routenname
    "{controller}/{action}/{id}", // URL mit Parametern
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // defaults
    new { controller = @"^(?!graphics).+" } // constraint
);

Upvotes: 2

Related Questions