Reputation: 837
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.
Upvotes: 1
Views: 3495
Reputation: 837
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.
Upvotes: 0
Reputation: 16348
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();
}
Upvotes: 0
Reputation: 9242
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.
Upvotes: 2