Как перенаправить запрос на каталог, который существует на диске, в ASP.NET MVC?

У меня есть приложение ASP.NET MVC 4 (с использованием .NET framework 4.5) с неограниченными URL-адресами. Сайт содержит некоторые статические файлы, но все запросы без расширения должны переходить на маршрутизацию MVC.

Все работает отлично для запросов вроде:

  • /
  • /news
  • /пт/Новости

Однако, если я делаю запрос для /fr, я получаю ошибку:

HTTP Error 403.14 - Forbidden, 
The Web server is configured to not list the contents of this directory. 

Я понимаю, что это потому, что на диске существует каталог /fr, но я все же хочу сопоставить этот запрос с моим MVC-приложением. Это не вариант удаления каталога fr, поскольку он содержит некоторые статические файлы.

Возможно ли это? Я попытался добавить runAllManagedModulesForAllRequests="true" в элемент modules в system.webServer(я действительно не хочу этого делать, но это все равно не помогло).

Изменить - в случае, если это полезно, вот маршрут:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.IgnoreRoute("cid/{*pathInfo}");
        routes.MapRoute(
           "Page",
           "{*PageId}",
           new { controller = "Page", action = "Page" }, // Parameter defaults
           new { pageId = @"^(.*)?$" } // Parameter constraints
        );
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }

Ответ 1

Самый простой способ предотвратить доступ к локальным папкам и файлам - установить флаг RouteCollection.RouteExistingFiles, чтобы ASP.NET обрабатывал URL-адреса, ориентированные на физические файлы. Поэтому измените свой регистр маршрута на:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("cid/{*pathInfo}");

    routes.RouteExistingFiles = true;
    routes.MapRoute(
       "Page",
       "{*PageId}",
       new { controller = "Page", action = "Page" }, // Parameter defaults
       new { pageId = @"^(.*)?$" } // Parameter constraints
    );
}

Ваш маршрут "Default" не требуется, так как "Page" - это действительно все-таки случай.

Другой метод - изменить конфигурацию IIS, чтобы предоставить ASP.NET MVC более высокий приоритет, чем список каталогов IIS. В IIS7, добравшись до вашего веб-сайта, выберите "Модули из раздела IIS", а затем "Просмотреть упорядоченный список из вкладки" Действия ". Переместите UrlRoutingModule выше DirectoryListingModule.


Как замечание, из ваших комментариев я понимаю, что у вас есть один контроллер с одним действием. Действие будет обслуживать все запросы, кроме статических ресурсов, определенных с помощью IgnoreRoute. Это не рекомендуется, так как вы теряете все преимущества архитектуры MVC. Кроме того, вы обнаружите, что ваша страница будет быстро расти, чтобы включать все больше и больше случаев. Это именно то, для чего были разработаны маршрутизаторы и контроллеры.

Если вы считаете, что единственный метод catch-all является лучшим решением для вас, вам не нужен MVC, и вам будет лучше использовать веб-API, который имеет гораздо меньшие накладные расходы.

Ответ 2

РЕДАКТИРОВАТЬ: Первоначально я думал, что необходимо удалить DirectoryListingModule, но это не так, мой пользовательский HttpModule переписывает перед этим, поэтому его можно оставить.

Обходной путь, который я использовал для исправления, состоял в том, чтобы добавить некоторую логику в пользовательский HttpModule, который выполнял некоторую обработку запроса. Здесь я обнаруживаю, соответствует ли запрос корню одной из моих локализаций (/fr//es/и т.д.), И если это так, перепишите URL-адрес по умолчанию /fr/index, для которого маршрутизация работает как обычно.

private void BeginRequest(object sender, EventArgs e)
{
    HttpApplication application = (HttpApplication)sender;
    HttpContext context = application.Context;
    var url = context.Request.Url.AbsolutePath;
    if (IsLocalizationRoot(url))
    {
        context.RewritePath(url + GetDefaultPageName());
    }
}

Если вы заинтересованы в том, как удалить DirectoryListingModule (как упоминалось выше, это необязательно, но его полезная информация в любом случае):

Как ссылается StaticFileHandler, вам нужно удалить его и удалить и повторно добавить StaticFileHandler (без DirectoryListingModule) в ваш web.config:

<system.webServer>
    <handlers>
      <remove name="StaticFile"/>
      <add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule" 
           resourceType="Either" requireAccess="Read" />  
    </handlers>
    <modules>
      <remove name="DirectoryListingModule"/>
    </modules>
</system.webServer>

Это, вероятно, вызовет ошибку HTTP 500 из-за нарушения блокировки. Вам нужно разблокировать его в IIS applicationHost.config:

откройте командную строку (запустите ее как Администратор) и перейдите в папку C:\Windows\system32\inetsrv

appcmd set module DirectoryListingModule /lockItem:false