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

Posted

技术标签:

【中文标题】ASP.Net MVC 路由捕获所有 *.aspx 请求【英文标题】:ASP.Net MVC route to catch all *.aspx requests 【发布时间】:2016-07-09 11:08:57 【问题描述】:

这个必须之前已经被问过,但是在阅读了here、here、here 和here 之后,我无法推断相关部分以使其工作。我正在将一个旧的 Web 表单站点改造成 MVC,并希望捕获特定的传入 HTTP 请求,以便我可以发出 RedirectPermanent(以保护我们的 Google 排名并避免用户因 404 而离开)。

我需要拦截所有以(或包含).aspx 文件结尾的请求,而不是拦截所有 传入请求,或解析某些id 值扩展名,例如

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 中设置吗?

【问题讨论】:

【参考方案1】:

我正在展示在 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 以像往常一样返回视图。如果浏览器因为 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 自动重定向用户。这两个都可以在浏览器中关闭,但用户很可能会去他们应该去的地方。如果没有,你应该告诉用户:

    页面有一个新位置。 如果没有自动重定向,他们需要点击链接。 他们应该更新他们的书签。

@
    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 添加一个部分,以便可以将元刷新添加到页面的头部部分。

<!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 请求,并在同一个控制器中处理它们all,该控制器只检查 URL,然后重定向到新的相应视图(因为整个站点结构不同)。如果没有匹配的视图,则返回友好的 404。这不仅仅是剥离 .aspx 扩展名的情况,因此我将从您的“奖励”代码开始。 @EvilDr - 我更新(更正)了我的答案。 1)有一个错误,它发送一个相对路径作为 301 重定向,它应该是绝对的 2)我修复了它,所以它通过提供一组路由值而不是硬编码 URL 将更加以 MVC 为中心。这样,如果您更改路由表中的 URL,它将自动更改 301 重定向以匹配它,因为它是从路由表生成的。 与 Dictionary 对象中的静态字符串 /Home/Contact 相比,您的更新 new controller = "Home", action = "Contact" 有什么优势?由于两者都不是强类型,因此如果控制器/方法名称发生更改,它无论如何都会中断,对吧? 例如,如果您决定将 [Route] 属性添加到您的操作方法之一,则 URL 将自动更改以匹配 [Route] 属性。但是,如果您要更改路由值列表,则需要更新 RedirectAspxPermanentRoute 配置。在 MVC 中,最好将 URL 生成留给路由表,因为这意味着您只在一个地方定义 URL。 确保更新 RedirectAspxPermanentRoute 类 - 构造函数签名以及它如何将 routeValues 传递给控制器​​。【参考方案2】:

试试这样的:

 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's blog的链接

【讨论】:

谢谢。这仅适用于www.sample.com/default.aspx,但当路径中包含目录时会失败。 对于每个 / 您需要添加另一个参数。 MVC 将斜杠后的每个字符串标记为新参数的一部分。我用其他可能的 url 映射编辑了答案。【参考方案3】:

由于我的情况,我只有几个主页,有 cockamamy 规则,我发现这更容易.. 创建一个“oldaspxcontroller”。所以我可以确定一切都正确映射。

//https://www.oldsite.com/topics/travel/page9.aspx
[HttpGet]
[Route("/topics/topic/pageoldpagenum.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 中,它工作得很好,甚至可以识别模式和页码..

【讨论】:

以上是关于ASP.Net MVC 路由捕获所有 *.aspx 请求的主要内容,如果未能解决你的问题,请参考以下文章

如何在 asp.net mvc 3 项目中路由 .aspx 页面?

如何使用 ASP.Net MVC 路由来路由图像?

ASP.NET MVC如何设置路由启动

混合 SPA 和 ASP.NET MVC 路由

asp.net mvc 中 要 访问/ Views/Admin/Order/Index.aspx页面 在 路由里该如何设置?

如何为 ASP.NET MVC 处理“未找到 404 页面”查询的全部路由?