ASP.Net MVC маршрут для перехвата всех *.aspx запросов

Должно быть, об этом уже спрашивали, но после прочтения здесь, здесь, здесь и здесь я не могу экстраполировать соответствующие части, чтобы заставить его работать. Я переоборудую старый сайт веб-форм в MVC и хочу перехватывать определенные входящие HTTP-запросы, чтобы я мог выдать RedirectPermanent (чтобы защитить наш рейтинг в Google и избежать ухода пользователей из-за 404-х).

Вместо того, чтобы перехватывать все входящие запросы или анализировать некоторые id значение, мне нужно перехватить все запросы, которые заканчиваются (или содержат) расширение файла .aspx, например

www.sample.com/default.aspx
www.sample.com/somedir/file.aspx
www.sample.com/somedir/file.aspx?foo=bar

Запросы к маршрутам MVC следует игнорировать (просто обрабатывать как обычно).

Вот что у меня так далеко, кроме ASPXFiles маршрут никогда не пробивается.

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

        // never generates a match
        routes.MapRoute(
            name: "ASPXFiles",
            url: "*.aspx",
            defaults: new { controller = "ASPXFiles", action = "Index" }
        );

        // Used to process all other requests (works fine)
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

}

Можно ли настроить этот тип маршрута в MVC?

2 ответа

Решение

Я показываю правильный способ сделать перенаправление 301 в MVC, так как не все браузеры правильно отвечают на запросы перенаправления 301, и вам нужно дать пользователю возможность продолжить, а не страницу "Перемещение объекта" по умолчанию, созданную ASP.СЕТЬ.

RedirectAspxPermanentRoute

Мы строим кастом RouteBase подкласс, который определяет, когда URL заканчивается .aspx и маршруты к нашему SystemController настроить перенаправление 301. Требуется передать карту URL-адреса (URL-адрес для сопоставления) для маршрутизации значений (которые используются для создания URL-адреса MVC).

public class RedirectAspxPermanentRoute : RouteBase
{
    private readonly IDictionary<string, object> urlMap;

    public RedirectAspxPermanentRoute(IDictionary<string, object> urlMap)
    {
        if (urlMap == null)
            throw new ArgumentNullException("urlMap");

        this.urlMap = urlMap;
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var path = httpContext.Request.Path;
        if (path.EndsWith(".aspx"))
        {
            if (!urlMap.ContainsKey(path))
                return null;

            var routeValues = urlMap[path];
            var routeData = new RouteData(this, new MvcRouteHandler());

            routeData.Values["controller"] = "System";
            routeData.Values["action"] = "Status301";
            routeData.DataTokens["routeValues"] = routeValues;

            return routeData;
        }

        return null;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        return null;
    }
}

Обратите внимание, что первая проверка для .aspx расширение, поэтому остальная часть логики будет полностью пропущена, если расширение не совпадает. Это обеспечит лучшую производительность для вашего сценария.

SystemController

Мы настраиваем SystemController чтобы вернуть вид, как мы обычно это делаем. Если браузер не перенаправляет из-за 301, пользователь увидит представление.

using System;    
using System.Net;
using System.Web;
using System.Web.Mvc;

public class SystemController : Controller
{
    //
    // GET: /System/Status301/

    public ActionResult Status301()
    {
        var routeValues = this.Request.RequestContext.RouteData.DataTokens["routeValues"];
        var url = this.GetAbsoluteUrl(routeValues);

        Response.CacheControl = "no-cache";
        Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
        Response.RedirectLocation = url;

        ViewBag.DestinationUrl = url;
        return View();
    }

    private string GetAbsoluteUrl(object routeValues)
    {
        var urlBuilder = new UriBuilder(Request.Url.AbsoluteUri)
        {
            Path = Url.RouteUrl(routeValues)
        };

        var encodedAbsoluteUrl = urlBuilder.Uri.ToString();
        return HttpUtility.UrlDecode(encodedAbsoluteUrl);
    }
}

Status301.cshtml

Следуйте соглашениям MVC и обязательно поместите это в /Views/System/ папка.

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

Представление будет пытаться перенаправить пользователя автоматически через JavaScript и через Meta-Refresh. Оба из них могут быть отключены в браузере, но есть вероятность, что пользователь сделает это там, где он должен идти. Если нет, вы должны сказать пользователю:

  1. Страница имеет новое местоположение.
  2. Им нужно нажать на ссылку, если они не перенаправлены автоматически.
  3. Они должны обновить свою закладку.

@{
    ViewBag.Title = "Page Moved";
}
@section MetaRefresh {
    <meta http-equiv="refresh" content="5;@ViewBag.DestinationUrl" />
}

<h2 class="error">Page Moved</h2>
<p>
    The page has moved. Click on the following URL if you are 
    not redirected automatically in 5 seconds. Be sure to update your bookmarks.
</p>
<a href="@ViewBag.DestinationUrl">@ViewBag.DestinationUrl</a>.

<script>
    //<!--
    setTimeout(function () {
        window.location = "@ViewBag.DestinationUrl";
    }, 5000);
    //-->
</script>

использование

Сначала вам нужно добавить раздел в ваш _Layout.cshtml поэтому Meta-refresh можно добавить в раздел заголовка вашей страницы.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - My ASP.NET MVC Application</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <!-- Add this so the view can update this section -->
        @RenderSection("MetaRefresh", required: false)
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>

    <!-- layout code omitted -->

</html>

Затем добавьте RedirectAspxRoute к вашей конфигурации маршрутизации.

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

        routes.Add(new RedirectAspxPermanentRoute(
            new Dictionary<string, object>() 
            {
                // Old URL on the left, new route values on the right.
                { @"/about-us.aspx", new { controller = "Home", action = "About" } },
                { @"/contact-us.aspx", new { controller = "Home", action = "Contact" }  }
            })
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Так как в моей ситуации у меня было только несколько главных страниц с дерзкими правилами, мне это показалось проще... Создать "oldaspxcontroller". Так что я могу быть уверен, что все правильно нанесено на карту.

//https://www.oldsite.com/topics/travel/page9.aspx
[HttpGet]
[Route("/topics/{topic}/page{oldpagenum}.aspx")]
public LocalRedirectResult TopicWithPage(string topic, string oldpagenum)
{

    return LocalRedirectPermanent($"/topics/{topic}?p={oldpagenum}");
}

Вы можете заметить, что я все еще использую pagenum в строке запроса... Я просто думаю, что это выглядит лучше... например, mysite.com/topics/travel?p=9 Мне нравится больше, чем mysite.com/topics/travel/page/9. Я использую.Net core 3.1, и он отлично работает, даже распознает шаблон и номер страницы..

Попробуйте что-то вроде этого:

 routes.MapRoute(
            name: "ASPXFilesWithFolderPath",
            url: "{folder}/{page}.aspx",
            defaults: new { controller = "ASPXFiles", action = "Index", folder=UrlParameter.Optional, page = UrlParameter.Optional }
        );
    routes.MapRoute(
            name: "ASPXFiles",
            url: "{page}.aspx",
            defaults: new { controller = "ASPXFiles", action = "Index", page = UrlParameter.Optional }
        );

Первоначально я собирался предложить и HTTPHandler, но расширение aspx отображается в IIS по умолчанию и, следовательно, не будет работать. Вот ссылка на блог Джона Галлоуэя

Другие вопросы по тегам