Reputation: 106826
Can ASP.Net routing (not MVC) be used to serve static files?
Say I want to route
http://domain.tld/static/picture.jpg
to
http://domain.tld/a/b/c/picture.jpg
and I want to do it dynamically in the sense that the rewritten URL is computed on the fly. I cannot set up a static route once and for all.
Anyway, I can create a route like this:
routes.Add(
"StaticRoute", new Route("static/{file}", new FileRouteHandler())
);
In the FileRouteHandler.ProcessRequest
method I can rewrite the path from /static/picture.jpg
to /a/b/c/picture.jpg
. I then want to create a handler for static files. ASP.NET uses the StaticFileHandler
for this purpose. Unfortunately, this class is internal. I have tried to create the handler using reflection and it actually works:
Assembly assembly = Assembly.GetAssembly(typeof(IHttpHandler));
Type staticFileHandlerType = assembly.GetType("System.Web.StaticFileHandler");
ConstructorInfo constructorInfo = staticFileHandlerType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
return (IHttpHandler) constructorInfo.Invoke(null);
But using internal types doesn't seem to be the proper solution. Another option is to implement my own StaticFileHandler
, but doing so properly (supporting HTTP stuff like ranges and etags) is non-trivial.
How should I approach routing of static files in ASP.NET?
Upvotes: 78
Views: 82572
Reputation: 11699
Why not use IIS to do this? You could create a redirect rule to point any requests from the first route to the second one before it even gets to your application. Because of this, it would be a quicker method for redirecting requests.
Assuming you have IIS7+, you would do something like...
<rule name="Redirect Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Redirect" url="/a/b/c/{R:1}" redirectType="Permanent" />
</rule>
Or, if you don't need to redirect, as suggested by @ni5ni6:
<rule name="Rewrite Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Rewrite" url="/a/b/c/{R:1}" />
</rule>
Edit 2015-06-17 for @RyanDawkins:
And if you're wondering where the rewrite rule goes, here is a map of its location in the web.config
file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<!-- rules go below -->
<rule name="Redirect Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Redirect" url="/a/b/c/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Upvotes: 59
Reputation: 1919
After digging through this problem for a few hours, I found that simply adding ignore rules will get your static files served.
In RegisterRoutes(RouteCollection routes), add the following ignore rules:
routes.IgnoreRoute("{file}.js");
routes.IgnoreRoute("{file}.html");
Upvotes: 40
Reputation: 11205
You need to add TransferRequestHandler for handling your static files.Please see following answer https://stackoverflow.com/a/21724783/22858
Upvotes: 4
Reputation: 624
I've had a similar problem. I ended up using HttpContext.RewritePath:
public class MyApplication : HttpApplication
{
private readonly Regex r = new Regex("^/static/(.*)$", RegexOptions.IgnoreCase);
public override void Init()
{
BeginRequest += OnBeginRequest;
}
protected void OnBeginRequest(object sender, EventArgs e)
{
var match = r.Match(Request.Url.AbsolutePath);
if (match.Success)
{
var fileName = match.Groups[1].Value;
Context.RewritePath(string.Format("/a/b/c/{0}", fileName));
}
}
}
Upvotes: 14
Reputation: 106826
I came up with an alternative to using the internal StaticFileHandler
. In the IRouteHandler
I call HttpServerUtility.Transfer
:
public class FileRouteHandler : IRouteHandler {
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
String fileName = (String) requestContext.RouteData.Values["file"];
// Contrived example of mapping.
String routedPath = String.Format("/a/b/c/{0}", fileName);
HttpContext.Current.Server.Transfer(routedPath);
return null; // Never reached.
}
}
This is a hack. The IRouteHandler
is supposed to return an IHttpHandler
and not abort and transfer the current request. However, it does actually achieve what I want.
Using the internal StaticFileHandler
is also somewhat a hack since I need reflection to get access to it, but at least there is some documentation on StaticFileHandler
on MSDN making it a slightly more "official" class. Unfortunately I don't think it is possible to reflect on internal classes in a partial trust environment.
I will stick to using StaticFileHandler
as I don't think it will get removed from ASP.NET in the foreseeable future.
Upvotes: 7