views:

447

answers:

4

Here's what I'd like to do. I have content that I am writing out to a view. This content has image references that are document relative. So for example, if I'm looking at the following URL:

http://localhost/article/8AB98/

The content might have an image in the following form:

<img src="myimage.png" />

This would obviously cause the browser to query for the image at the following URL:

http://localhost/article/8AB98/myimage.png

However, because of the mvc routing, this image would not be found. Do you know of a simple way that I can cause that URL to return the correct image to the browser?

please note: it's actually important that the markup remain untouched from the original ... this means that somehow re-writing image urls so they point to another folder outside of the current view's URL is unfortunately out of the question.

Thanks!!

+1  A: 

One way to do it would be to put in an IgnoreRoute for the image files:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("article/{ArticleID}/{name}.png");
    ...
Robert Harvey
+6  A: 

You can use the Url.Content() method.

<img src="<%= Url.Content("~/images/myimage.png") %>" />

That will resolve the url from the application root.

Haacked
I'd be tempted to write a helper as well so that you could just have Html.Image("~/images/myimage.png", "myAltText"); And your helper method would do return basically what Phil's code has.
Dan Atkinson
Hi Phil ... changing the URL from "myimage.png", to what url.content would return is not possible, because the content's markup is not mine to change. Aside from that fact, I actually need this markup (<img src="myimage.png" />) to be rendered to the browser (as @thinkzig mentioned)
Joel Martinez
+4  A: 

I'm assuming that when you say "it's actually important that the markup remain untouched from the original" you mean that

<img src="myimage.png" />

is what must be rendered to the browser and so you need to trick the web server into taking the request URL of

http://localhost/article/8AB98/myimage.png

and use only that information to find the correct image, wherever you have it stored, and return it to the browser.

Two options come to mind, but it's hard to know which to recommend because you haven't said where the images are being stored.

Option 1 - Url Rewriter

Buy a copy of ISAPI_Rewrite and have all urls that meet the above criteria rewritten so that they go get the image wherever it lives. More on ISAPI_Rewrite here.

Option 2 - Custom HttpHandler

You could write an HttpHandler mapped to all PNG file requests that parses the request URL and does what it needs to do to find the image, then return it to the response stream. The downside of this is that you'd have to tell IIS to map all PNG requests to go through the aspnet_isapi.dll which might be a performance bummer.

I'm still not sure if I understand your problem correctly but I hope this helps. Good luck.

thinkzig
I didn't use exactly your two options, but this was ultimately the solution that resolved it :-) I'll post another answer to the question with the details
Joel Martinez
A: 

I ended up using @thinkzig's solution, albeit in a slightly different fashion. Using the FileContent from the MVC Futures assembly, I just added another route to handle images.

routes.MapRoute("Image", "article/{id}/{image}", new { controller = "Article", action = "Image" });
routes.MapRoute("Article", "article/{id}", new { controller = "Article", action = "Index" });

This new action method simply constructs the filepath based on the articleID and image name:

public ActionResult Image(string id, string image)
{
    string articlePath = Server.MapPath("~/views/article/");
    string filePath = Path.Combine(articlePath, string.Format("{0}/{1}", id, image));
    return this.File(filePath, "image");
}

There was one other little thing I had to contend with. If the user accesses the article without a trailing slash (http://localhost/article/8AB98), then the browser thinks that the articleID is the file, and tries to find the image wrong folder (http://localhost/article/img.png).

Thankfully in that case, mvc routes the request to the Article action with the image name as the "id" parameter, so I can simply look for a "." in the id and then use the regular Image action to process it.

in the Article action:

if (id.Contains("."))
{
    return RedirectToImage(id);
}

And then the redirecttoimage code which figures out the ID and file name

private ActionResult RedirectToImage(string id)
{
    if (Request.UrlReferrer == null)
    {
        return Content("invalid request");
    }

    var referrer = Request.UrlReferrer.ToString();
    if (referrer.Contains("?"))
    {
        referrer = referrer.Split('?')[0];
    }

    var realId = Path.GetFileName(referrer);
    return this.Image(realId, id);
}

You'll note that I rely on the url referrer to get the actual article ID. If the user tries to right click on the image (when viewed without the trailing slash) and chooses "open image in new tab", then I have no way of knowing what the article ID is, so I just return an "invalid request" string to the user. That's fine because I'm not really trying to support those users in that situation :-)

Joel Martinez