在自定义ASP MVC路由中别名URL

时间:2014-05-02 00:16:08

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

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

我将这个装入一个项目,该项目包含映射到自定义Route的catch-all URL,该URL解析传入的URL字符串并查找数据库中的URL部分。基本上,整个URL字符串是动态的并映射到类别层次结构(例如/ tires / car / tires / van,/ suspension / ford / focus)。这一切都运行正常,只存在路由到一个控制器。

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

我认为更好的方法是映射两条独立的路线。一个是 URLAliasRoute ,另一个是 CustomWebsiteRoute 。路由是否可能影响其他路由将看到的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 

并且网址汽车/轮胎别名为汽车轮胎

因此,当收到 / car-tires 的传入请求时,我的UrlAliasRoute会在数据库中查找,看到它有“汽车/轮胎”的映射,然后更改RouteData URL从“汽车轮胎”到“汽车/轮胎”。然后所有其他路由使用 car / tires 作为匹配路由约束的URL?

这也有利于允许开发人员编码的任何预定义路由的别名......有潜在危险,但我会小心。

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

1 个答案:

答案 0 :(得分:2)

我通常使用如下方法。首先在靠近顶部的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然后可以在数据库中包含查找,但是最近我在项目中将其作为HTMLHelper,它也允许我传入直接来自数据库列的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;
}

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

通过早期使用此路由,您可以将旧网址重定向到其他路由,然后在请求级联路由表时触发这些路由。最终,您将最终处于默认路线,类似于:

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

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