ASP.NET Core 中的SEO優化(3):自定義路由匹配和生成


前言

前兩篇文章主要總結了CMS系統兩個技術點在ASP.NET Core中的應用:

而本篇文章,繼續介紹另一個技術點:自定義路由匹配和生成。

背景

在MVC5時代,默認的路由可能就是簡單的約定/{controller}/{action}/{id},第一節對應控制器(Controller)名,第二節對應操作(Action)名,第三節是參數名。
在WebApi和ASP.NET Core時代,有了Route特性來指定相應操作的路由指向,可以很靈活地配置RESTful Api。

但是,路由靈活性在注重SEO的CMS系統中有更苛刻的要求。例如:

  • 欄目的列表 ->/{父欄目名}/{子欄目名}-{頁碼}/
  • 文章詳情頁 ->/{欄目名}/{文章名}.html
  • 標簽頁 ->/{標簽名}

這些友好的url鏈接使用默認的路由約定是很難實現的,當然是可以配置路由規則去傳遞參數:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "article_list",
        template: "{parentCategory}/{category}-{page}/",
        defaults: new { controller = "Article", action = "Index" });

    routes.MapRoute(
        name: "article_detail",
        template: "{category}/{article}.html",
        defaults: new { controller = "Article", action = "Detail" });

    routes.MapRoute(
        name: "tags",
        template: "{tag}/",
        defaults: new { controller = "Article", action = "Tag" });
    });

但是,這樣配置很繁瑣,也不靈活,如果還要傳入更多規則,比如不向下匹配,就顯得捉襟見肘了。那有沒有更靈活的方案呢?當然有,就是本文接下來要介紹的IRouter接口。

原理

上一節最后介紹的一般路由配置方法,其實是MapRoute方法創建一個個IRouter的實例,添加到IRouteBuilder實例中,具體方法可以看看源碼

所以我們可以實現一個自定義的IRouter,就能不用默認的約定,去實現我們的苛刻需求。

首先要看看IRouter這個接口的定義,源碼在aspnet/Routing/src/Microsoft.AspNetCore.Routing.Abstractions/IRouter.cs

namespace Microsoft.AspNetCore.Routing
{
    public interface IRouter
    {
        Task RouteAsync(RouteContext context);

        VirtualPathData GetVirtualPath(VirtualPathContext context);
    }
}

實現

IRouter接口只有兩個方法,本文分別給出代碼示例來介紹。

RouteAsync

這個方法中實現路由匹配,從路由上下文中獲取所需要的數據,存入RouteData字典中,再從控制器取出做相應的處理。

public async Task RouteAsync(RouteContext context)
{
    var requestedUrl = context.HttpContext.Request.Path.Value.TrimStart('/').ToLower();
    var split = requestedUrl.Split('/');

    if (secoend != null && secoend.EndsWith(".html") && split.Length == 2)
    {
        var title = secoend.Replace(".html", "");
        context.RouteData.Values["controller"] = "Article";
        context.RouteData.Values["action"] = "Detail";
        context.RouteData.Values["category"] = first;
        context.RouteData.Values["title"] = title;
    }
    //...對請求路徑進行一系列的判斷

    //最后注入`MvcRouteHandler`示例執行`RouteAsync`方法,表示匹配成功
    await context.HttpContext.RequestServices.GetService<MvcRouteHandler>().RouteAsync(context);
}

這樣,當請求路由為/news/asp.net.html時,就會匹配到上面的規則,請求進入Article控制器中的Detail操作被處理,並且可以從控制器中的RouteData.Values["category"]?.ToString();方法拿到所需的數據。

GetVirtualPath

這個方法實現路由生成,可以從路由上下文中獲取RouteData字典中的數據,進行虛擬路徑(區別與真實目錄)的生成。

public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
    var path = string.Empty;
    var hasController = context.Values.TryGetValue("controller", out var controller);
    var hasAction = context.Values.TryGetValue("action", out var action);
    var hasCategory = context.Values.TryGetValue("category", out var category);
    var hasTitle = context.Values.TryGetValue("title", out var title);

    if (hasController && hasAction && hasCategory && hasTitle)
    {
        path = $"/{category/{title}.html";
    }

    return path != string.Empty ? new VirtualPathData(this, path) : null;
}

這樣,當調用路徑生成方法@Url.Action("Detail","Article",new { title="asp.net", category="news" }),就會生成"/news/asp.net.html"這樣的路徑來。

IRouter的設置生效

自定義實現的IRouter如何設置到原有的MVC項目中呢?方法很簡單,上面已經簡單說道了,其實app.UseMvc這個方法就有添加IRouter的方法:

app.UseMvc(routes =>
{
    //添加 自定義路由匹配與url生成組件
    routes.Routes.Add(new RouteProvider());
});

這里RouteProviderIRouter的實現,這樣自定義的路由提供對象就生效啦!

相關小技巧

  1. SEO中會有一條鐵規則,就是不是有效鏈接的情況下只能返回404,比如,手動輸入了一個路徑,能匹配到文章詳情的操作(action),如果數據庫中查不出文章,本來應該不能匹配的,但是,如果在匹配的時候查詢數據庫確認是否存在的話,會加大系統的壓力,所以,可以放在操作(action)中查詢,查不到再返回NotFound,讓上一篇文章《ASP.NET Core 中的SEO優化(2):中間件中渲染Razor視圖》中介紹的中間件一並處理輸出404頁面。

  2. 站內鏈接如果使用帶域名的絕對路徑,能夠提高優化效果,我們可以自己寫一個UrlHelper的擴展方法:

public static class UrlHelperExtensions
{
    public static string AbsoluteAction(
        this IUrlHelper helper,
        string actionName,
        string controllerName,
        object routeValues = null)
    {
        string scheme = helper.ActionContext.HttpContext.Request.Scheme;
        return helper.Action(actionName, controllerName, routeValues, scheme);
    }

    public static string AbsoluteContent(
        this IUrlHelper helper,
        string contentPath)
    {
        return new Uri(helper.ActionContext.HttpContext.Request.GetUri(), helper.Content(contentPath)).ToString();
    }

    public static string AbsoluteRouteUrl(
        this IUrlHelper helper,
        string routeName,
        object routeValues = null)
    {
        string scheme = helper.ActionContext.HttpContext.Request.Scheme;
        return helper.RouteUrl(routeName, routeValues, scheme);
    }
}

總結

本文主要介紹了自定義路由匹配和生成的解決方案,把路由相關的處理集中到一個類中,避免分散在各個視圖進行維護。下篇文章,將會介紹自定義視圖搜索目錄及主題切換。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM