1

In my ASP.NET MVC (3) application, I have setup the following route in global.asax.cs:

routes.MapRoute(
    "UniqueId",
    "{uniqueId}",
    new { controller = "Book", action = "DownloadBook" },
    new { uniqueId = "[0-9a-zA-Z]{5}" }
);

The DownloadFile action method is:

public ActionResult DownloadBook(string uniqueId)
{
    string path = Server.MapPath(String.Format("~/App_Data/Books/{0}/index.htm", uniqueId));
    if (System.IO.File.Exists(path))
    {
        return File(path, "text/html");
    }
    return new EmptyResult();
}

The method correctly serves the index.htm file from the subdirectory in the /App_Data/Books directory with the name that corresponds to the uniqueId that is defined in the route. However, the CSS and image files in the index.htm file cannot be found, since the browser tries to find them in the original URL location (e.g. http://localhost/3Yru3/).

Is there anything I can do about this? I am probably overlooking something?

EDIT (also see my comments in the answers to my question):
The books are stored as HTML files (and not as MVC Views, which would make referring to the CSS and images less of a problem) because:
1. They will be uploaded as such by users.
2. I want to store the index.htm file and the resources it uses in an HTML5 appcache, to be available offline.

EDIT 2 I have found a solution to my own question and would like to know what you think of it. See my own answer in the answers below.

3 Answers 3

2

This must be happening because you are referencing your files in a wrong way (relatively), meaning that it will be served according to the root of your current page.

Ex:

<link rel="Stylesheet" href="/Styles/site.css" />

or

<link rel="Stylesheet" href="../../Styles/site.css" />

Instead of this way, use the following sytax to link you CSS files and your images

<link rel="Stylesheet" href="<%=Url.Content("~/Styles/site.css")%>" />

This should work fine and be evaluated correctly from any page regardless of it's location.

Sign up to request clarification or add additional context in comments.

6 Comments

The CSS reference in the index.htm file is: <link type="text/css" rel="stylesheet" href="index.css" /> (the index.htm file and its resources are located in the same folder). Since I am serving an html file, I cannot use the .NET syntax you describe to refer to the CSS file.
It would be nice if somehow the browser could be instructed to retrieve the CSS and image files from the same directory as where the index.htm file is ACTUALLY located (~/App_Data/Books/), and not from the request URL (localhost/3Yru3). But that must be impossible since the App_Data folder cannot be referred to directly. That is the reason I have chosen it in the first place. I only want users to be able to download books using a unique ID like 3Yru3 in the query string.
You can actually use the absolute path syntax that Mohammed suggested, even if it is from an HTML page. Another alternative is to convert your HTML page to an aspx/Razor page, and then you can use any of the suggested answers.
The thing to note is that the browser interprets images and other resources in the CSS file as a path relative to that CSS file's location. It isn't relative to the page's URL, it is relative to the CSS file's path. So any way you can manage to successfully include a link to the CSS file, the images/other stuff in the CSS file will work.
@Charlie Thanks for your comments. I cannot convert the index.htm page to an ASPX or Razor page, like you suggest, because I want it to be stored in the HTML5 appcache (to make it available offline).
|
0

You can do something, but it's more of a hack. Put placeholders for the css link then, when serving the file, do a replace, something like this.

public ActionResult DownloadBook(string uniqueId)
{
string path = Server.MapPath(String.Format("~/App_Data/Books/{0}/index.htm", uniqueId));
if (System.IO.File.Exists(path))
{
   var file=File.ReadAllText(path);
   //this needs a bit more refining, it's just a proof of concept
     //you can use Razor templating, there is a library for that
    file=file.Replace("{CssHref}",UrlHelper.GenerateContentUrl("~/Content/site.css",HttpContext));
   return Content(file);
}
return new EmptyResult();
}

4 Comments

If I cannot find a solution that is less of a 'hack' I may give this a try. It contains some creative thinking.
Another solution is to actually store the books html as Views for a Book controller. They aren't directly accesible via the browser and they will be parsed by the view engine. That's also the most simple solution
Good suggestion, but this will not allow me to add the book to an HTML5 appcache, if I am correct? See also my response to the comment of Charlie in another answer. I will update my question to reflect the additional requirements I have.
If I understood correctly the html5 appcache is a client side thing. So it doesn't matter how the server will serve the page, as long it sends also the cache manifest. There are separate things, the cache deals with a received html document, the server generates the html document.I don't know about the uploading part, if you can upload file directly in the views folder, that needs testing
0

I think I may have found a solution to my problem that is quite elegant, but I would like to hear from you what you think of it. I have been looking at 'classic' URL rewriting in trying to send the browser to the right location, when it attempts to GET CSS files and image files that are referred to by the index.htm file in the App_Data subfolder (that has become a long sentence, I hope it makes sense ;-).

First I have tried to use Context.RewritePath in the BeginRequest event handler of the global.asax.cs of the app. That gave some unexpected side effects. Then I created a custom route class that covers both the original route described in my question above and URL rewriting for the CSS and images files requests. The custom route uses the fact that during those requests the Request.UrlReferrer property contains the URL of the location of the index.htm file. Since this is becoming a very long story, I refer to a blog post that I have written on this subject for the technical details. I hope this information will save others some valuable time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.