ASP.Net MVC路由捕获所有*.aspx请求

5
这个问题可能已经被问过了,但在阅读了这里, 这里, 这里这里之后,我无法推断出相关的部分以使其正常工作。我正在将一个旧的网页表单站点改为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中是否可以设置?
3个回答

3
我展示了在MVC中进行301重定向的正确方法,因为不是所有浏览器都能正确响应301重定向请求,你需要给用户一个继续的选项,而不是由ASP.NET生成的默认的“对象已移动”页面。

RedirectAspxPermanentRoute

我们创建了一个自定义的RouteBase子类,它检测URL是否以.aspx结尾,并将其路由到我们的SystemController以设置301重定向。它需要你传递一个URL映射(要匹配的URL)和路由值(用于生成MVC URL)。
public class RedirectAspxPermanentRoute : RouteBase
{
    private readonly IDictionary<string, object> urlMap;

    public RedirectAspxPermanentRoute(IDictionary<string, object> urlMap)
    {
        this.urlMap = urlMap ?? throw new ArgumentNullException(nameof(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 }
        );
    }
}

哇,非常全面,谢谢。我需要一段时间来消化这个...目标是捕获所有.aspx请求,并在同一个控制器中处理它们,该控制器仅检查URL然后重定向到新的相应视图(因为整个站点结构都不同)。如果没有匹配的视图,则返回友好的404。这不是仅仅去掉.aspx扩展名的情况,所以您的“奖励”代码是我开始的地方。 - EvilDr
@EvilDr - 我更新(更正)了我的答案。1)它存在一个错误,即将相对路径作为301重定向发送,而应该是绝对路径2)我通过提供一组路由值而不是硬编码URL来修复了它,这样,如果您在路由表中更改URL,它会自动更改301重定向以匹配它,因为它是从路由表生成的。 - NightOwl888
1
例如,如果您决定将[Route]属性添加到其中一个操作方法中,则URL将自动更改以匹配[Route]属性。但是,如果您更改了路由值列表,则需要更新“RedirectAspxPermanentRoute”配置。在MVC中,最好将URL生成留给路由表,因为这意味着您只需在一个地方定义URL。 - NightOwl888
1
请确保您更新了 RedirectAspxPermanentRoute 类 - 构造函数签名已更改,以及它如何将路由值传递给控制器。 - NightOwl888
如果您的键不区分大小写,请在字典定义中添加 StringComparer.OrdinalIgnoreCase。因此,在您的 RouteConfig 中,使用 new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) - CrnaStena
显示剩余2条评论

0
尝试像这样做:

 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中默认映射,因此无法使用。这里有一个链接到Jon Galloway的博客


谢谢。这仅适用于 www.sample.com/default.aspx,但在路径中包含目录时会失败。 - EvilDr
对于每个斜杠,您需要添加另一个参数。MVC将斜杠后面的每个字符串标记为新参数的一部分。我已编辑答案,包括其他可能的URL映射。 - edita

0

由于我的情况,我只有几个主要页面,规则非常混乱,我发现这样更容易。创建一个“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 中,并且它工作得很好,甚至可以识别模式和页码..

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