使用ASP.NET路由来提供静态文件

78
ASP.Net路由(非MVC)可以用于提供静态文件吗?
比如说我想路由:
http://domain.tld/static/picture.jpg

为了

http://domain.tld/a/b/c/picture.jpg

我希望您能够动态地进行翻译,即实时计算重写后的URL。我不能一劳永逸地设置静态路由。
无论如何,我可以创建以下路由:
routes.Add(
  "StaticRoute", new Route("static/{file}", new FileRouteHandler())
);

FileRouteHandler.ProcessRequest方法中,我可以将路径从/static/picture.jpg重写为/a/b/c/picture.jpg。然后,我想为静态文件创建处理程序。ASP.NET使用StaticFileHandler来实现此目的。不幸的是,这个类是内部的。我已经尝试使用反射创建处理程序,它确实起作用:
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);

但是使用内部类型似乎不是正确的解决方案。另一个选项是实现自己的StaticFileHandler,但要做到正确(支持HTTP的范围和ETags等)是非常困难的。

我应该如何处理ASP.NET中静态文件的路由?

5个回答

58

为什么不使用IIS来完成这个任务?您可以创建一个重定向规则,将任何来自第一个路由的请求指向第二个路由,甚至在请求到达您的应用程序之前。因此,这是一种更快的重定向请求的方法。

假设您有IIS7+,您可以执行以下操作...

<rule name="Redirect Static Images" stopProcessing="true">
  <match url="^static/?(.*)$" />
  <action type="Redirect" url="/a/b/c/{R:1}" redirectType="Permanent" />
</rule>

或者,如果你不需要重定向,就像 @ni5ni6 建议的那样:

<rule name="Rewrite Static Images" stopProcessing="true">
  <match url="^static/?(.*)$" />
  <action type="Rewrite" url="/a/b/c/{R:1}" />
</rule>

2015年6月17日为@RyanDawkins编辑:

如果你想知道重写规则放在哪里,这是它在web.config文件中位置的映射。

<?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>

我也更喜欢这种方法。将其移出到管道中,让IIS工作进程处理重定向比通过运行时实际处理路由更有效,我想。即使服务器上的磨损量大致相同,我也会选择不使用路由并将重定向保持在应用程序之外。 - Joseph Ferris
1
马丁,除了性能问题之外,为什么你想在应用程序中处理这个URL,而不是在外部处理呢?我想你可以创建一个控制器操作来处理它,但是这里有一些东西你可以看看:- http://geekswithblogs.net/sankarsan/archive/2009/01/18/developing-custom-routehandler.aspx - Dan Atkinson
1
@RyanDawkins 在 web.config 文件中。请查看我的编辑以获取有关其确切位置的更多信息。 - Dan Atkinson
1
很棒的答案,一次就成功了。这种情况并不经常发生! - dahui
正是我在React应用程序中需要的客户端路由。 - William Herrmann
显示剩余3条评论

39

在花费数小时研究后,我发现只需添加忽略规则即可让静态文件得到提供。

在RegisterRoutes(RouteCollection routes)中,添加以下忽略规则:

routes.IgnoreRoute("{file}.js");
routes.IgnoreRoute("{file}.html");

对我来说可以工作,但是我无法解析区域视图文件夹中的JS文件。为了解决这个问题,我使用另一个文件夹,而不是区域基础文件夹中的/ Areas / MyArea / ClientScripts / foo.js。 - eka808
30
我好像在拼图中缺少一块...忽略静态文件路径是如何帮助从 /static/a/b/c 的路由(这是提问者询问的问题)?你能否对你的答案进行更详细的解释,我真的很想了解这个解决方案。 - Oliver
21
同意奥利弗的观点,这篇回答未能解决问题,不能被接受为解决方案。 - dhochee
如果您需要在路由之前让MVC控制器首先检查用户是否已登录,该怎么办?忽略路由并不等同于重定向到静态文件夹结构。 - pilavdzice
最简单的方法添加您的robots.txt文件 - joverall22

13

我有类似的问题。最终我使用了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));
        }
    }
}

1
非常感谢您的分享。我在寻找本地开发期间URL重写的解决方案时遇到了这个,意识到我可以在任何地方都使用它。我将其用于JS和CSS文件版本管理,而不是输入解析。 - Brendan Moore
对于任何感兴趣的人,为了管理形式为filename.v1234.js的文件,我正在使用private readonly Regex regJS = new Regex("^(.*)([.]v[0-9]+)([.](js|css))$", RegexOptions.IgnoreCase);,然后在匹配上进行Context.RewritePath(string.Format("{0}{1}", matchJS.Groups[1].Value, matchJS.Groups[3].Value));操作。 - Brendan Moore

7

我想到了一种替代使用内部 StaticFileHandler 的方法。在 IRouteHandler 中,我调用了 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.
  }

}

这是一个黑客技巧。 IRouteHandler 应该返回一个 IHttpHandler 而不是中止并传输当前请求。然而,它确实实现了我想要的功能。
使用内部的 StaticFileHandler 也有点 hack,因为我需要反射来获取它的访问权限,但至少有一些关于MSDN上StaticFileHandler的文档,使它成为一个稍微更“正式”的类。不幸的是,在部分信任环境中,我认为不可能反射内部类。
我将坚持使用 StaticFileHandler,因为我认为它不会在可预见的未来从ASP.NET中移除。

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接