Использование маршрутизации ASP.NET для обслуживания статических файлов

Может ли 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?

Ответ 1

После прохождения этой проблемы в течение нескольких часов я обнаружил, что просто добавление правил игнорирования приведет к загрузке ваших статических файлов.

В RegisterRoutes (маршруты RouteCollection) добавьте следующие правила игнорирования:

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

Ответ 2

Почему бы не использовать 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-06-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>

Ответ 3

У меня была аналогичная проблема. Я закончил использование 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));
        }
    }
}

Ответ 4

Я придумал альтернативу использованию внутреннего 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 также немного взломано, так как мне нужно отражение, чтобы получить к нему доступ, но по крайней мере есть документация на StaticFileHandler на MSDN, что делает его немного более "официальным" классом. К сожалению, я не думаю, что можно подумать о внутренних классах в частичной среде доверия.

Я буду придерживаться использования StaticFileHandler, поскольку я не думаю, что он удаляется из ASP.NET в обозримом будущем.

Ответ 5

Вам необходимо добавить TransferRequestHandler для обработки ваших статических файлов. Пожалуйста, см. следующий ответ fooobar.com/info/90741/...