【问题标题】:Aliasing a URL inside a custom ASP MVC Route在自定义 ASP MVC 路由中为 URL 起别名
【发布时间】:2014-06-18 14:07:21
【问题描述】:

我正在创建 URL 别名功能;将资源 A 映射到资源 B 的能力。

我正在将此应用到一个项目中,其中包含一个映射到自定义路由的包罗万象的 URL,该路由解析传入的 URL 字符串并在我们的数据库中查找 URL 部分。基本上,整个 URL 字符串是动态的并映射到类别层次结构(例如 /tyres/car /tyres/van、/suspension/ford/focus)。这一切都很好,并且只存在路由到一个控制器。

目前我正在将我的 URL 别名代码集成到此路由中,因为我们目前只允许这些动态路由的别名。它查找我们数据库的传入 URL 以查看是否存在别名,然后获取它映射到的原始 URL 并将该字符串传递给我们的 URL 解析器。这感觉很脏,关注点分离等等。

我认为更好的方法是映射两条单独的路线。一个是 URLAliasRoute,另一个是 CustomWebsiteRoute。 Route 是否可能影响其他路由将看到的 URL?

所以,想象一下我映射了以下路线:

UrlAliasRoute aliasRoute = new UrlAliasRoute("{*url}");
routes.Add(aliasRoute );
CustomWebsiteRoute customRoute = new CustomWebsiteRoute("{*url}");
routes.Add(customRoute );
routes.MapRoute("Contact Us", "contact", new { controller = "Contact" });
routes.MapRoute("About Us", "about", new { controller = "About" });
//etc 

和 URL car/tyres 别名为 car-tyres

所以当 /car-tyres 的传入请求进来时,我的 UrlAliasRoute 会在数据库中查找它,看到它有一个“car/tyres”的映射,然后更改RouteData URL 从“car-tyres”到“car/tyres”。然后所有其他路线都使用 car/tyres 作为 URL 来匹配路线约束?

这还有一个好处是允许对开发人员编写的任何预定义路由进行别名...可能很危险,但我会小心。

似乎是一个不错的解决方案,但我一直在努力寻找如何更改调用 GetRouteData() 后其他路由将收到的 URL。

【问题讨论】:

    标签: asp.net-mvc url-rewriting asp.net-mvc-routing


    【解决方案1】:

    我一般使用如下方法。首先在顶部附近的 Routeconfig 中,我注册了一个新的 Routebase(称为 LegacyURLRoute):

    routes.Add(new LegacyUrlRoute());
    

    一个缩减版如下:

    public class LegacyUrlRoute : RouteBase
    {
        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            var request = httpContext.Request;
            var response = httpContext.Response;
            var legacyUrl = request.Url.ToString().ToLower();
            var legacyPath = request.Path.ToString().ToLower();
    
            Tuple<bool, bool, string> result = DervieNewURL(legacyPath);
            bool urlMatch = result.Item1;
            bool urlMatchGone = result.Item2;
            var newUrl = result.Item3;
    
            if (urlMatch)
            {//For 301 Moved Permanently
                response.Clear();
                response.StatusCode = (int)System.Net.HttpStatusCode.MovedPermanently;
                response.RedirectLocation = "http://www.example.com" + newUrl;
                response.End();
            }
            else if (urlMatchGone)
            {// 410 Gone
                response.Clear();
                response.StatusCode = (int)System.Net.HttpStatusCode.Gone;
                response.End();
            }
            return null;
        }
        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            return null;
        }
    }
    

    上面的 DervieNewURL 然后可以在 DB 中包含查找,但最近在一个项目中,我将它作为 HTMLHelper 使用,它还允许我传入直接来自 DB 列的 URL,然后可以对这些进行解析和酌情更新。

    DervieNewURL 的简单示例如下所示,但您显然会在表格中查找,而不是像下面这样硬编码位。

    public static Tuple<bool, bool, string> DervieNewURL(string url)
    {
        /*
            return type from this function is as follows:
            Tuple<bool1, bool2, string1, string2>
            bool1 = indicates if we have a replacement url mapped
            bool2 = indicates if we have a url marked as gone (410)
            string1 = indicates replacement url
            */
        Tuple<bool, bool, string> result = new Tuple<bool, bool, string>(false, false, null);
        if (!String.IsNullOrWhiteSpace(url))
        {
            string newUrl = null;
            bool urlMatch = false;
            bool urlMatchGone = false;
    
            switch (url.ToLower())
            {
                case "/badfoldergone/default.aspx": { urlMatchGone = true; } break;
                case "/oldfoldertoredirect/default.aspx": { urlMatch = true; newUrl = "/somecontroller/someaction"; } break;
                default: { } break;
            }
            result = new Tuple<bool, bool, string>(urlMatch, urlMatchGone, newUrl);
    
        }
        return result;
    }
    

    如果您需要通配符匹配,那么我想您可以修改上述内容或将其构建到数据库调用匹配条件中。

    通过尽早使用此路由,您可以将旧版 url 重定向到其他路由,然后当请求沿路由表向下级联时触发这些路由。最终你会在你的默认路线结束,类似于:

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

    如果开发人员有硬编码链接,那么您只需要使用将由这些 url 触发的路由。通过采用上述方法,可以尽早捕获并添加到数据库中的列表中,并在需要时添加适当的 301 Moved 或 410 gone 。

    【讨论】:

    • 非常感谢您的详细解答!不幸的是,我已经将所有 URL 别名的东西都放入了我的 CustomWebsiteRoute,因为我有一些奇怪的需求,在这里更容易完成。不过,您的回答仍然很有帮助,再次感谢。
    • GD 阅读,我正在尝试做类似的事情来说明 github 在重命名容器时所做的事情,github.com/aspnet/EntityFramework6github.com/dotnet/ef6 是我试图弄清楚的一个完美例子。基本上他们必须保持映射 x 到 y,但是如果你现在有能力再次拥有 x,那么......是否只是将名称作为别名保存在数据库中,直到有其他东西使用它?寻找如何实现他们的重定向......他们有多少表的实习生?一个用于主条目,一个用于别名?然后当新来的时候删除别名?,所以...
    • ...当你创建一个新的 repo 时,他们会检查是否有任何别名指向不同的 repo?
    猜你喜欢
    • 1970-01-01
    • 2012-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多