前言

前两篇文章主要总结了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);
}
}

总结

本文主要介绍了自定义路由匹配和生成的解决方案,把路由相关的处理集中到一个类中,避免分散在各个视图进行维护。下篇文章,将会介绍自定义视图搜索目录及主题切换。

ASP.NET Core 中的SEO优化(3):自定义路由匹配和生成的更多相关文章

  1. ASP.NET Core 中的SEO优化(4):自定义视图路径及主题切换

    系列回顾 <ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存> <ASP.NET Core 中的SEO优化(2):中间件中渲染Razor视图> < ...

  2. ASP.NET Core 中的SEO优化(2):中间件中渲染Razor视图

    前言 上一篇文章<ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存>中介绍了中间件的使用方法.以及使用中间件实现服务端静态化缓存的功能.本系列文章的这些技巧都是我 ...

  3. ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存

    分享 最近在公司成功落地了一个用ASP.NET Core 开发前台的CMS项目,虽然对于表层的开发是兼容MVC5的,但是作为爱好者当然要用尽量多的ASP.NET Core新功能了. 背景 在项目开发的 ...

  4. Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式

    一.前言 上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 授权中心之应用实战 的文章,其中有不少博友给我提了问题,其中有一个博友问我的一个场景,我给他解答的还不够完 ...

  5. ASP.NET Core中使用Cache缓存

    ASP.NET Core中使用Cache缓存 缓存介绍: 通过减少生成内容所需的工作,缓存可以显著提高应用的性能和可伸缩性. 缓存对不经常更改的数据效果最佳. 缓存生成的数据副本的返回速度可以比从原始 ...

  6. Asp.Net Core 中IdentityServer4 授权原理及刷新Token的应用

    一.前言 上面分享了IdentityServer4 两篇系列文章,核心主题主要是密码授权模式及自定义授权模式,但是仅仅是分享了这两种模式的使用,这篇文章进一步来分享IdentityServer4的授权 ...

  7. Asp.Net Core 中IdentityServer4 实战之 Claim详解

    一.前言 由于疫情原因,让我开始了以博客的方式来学习和分享技术(持续分享的过程也是自己学习成长的过程),同时也让更多的初学者学习到相关知识,如果我的文章中有分析不到位的地方,还请大家多多指教:以后我会 ...

  8. Asp.Net Core 中IdentityServer4 实战之角色授权详解

    一.前言 前几篇文章分享了IdentityServer4密码模式的基本授权及自定义授权等方式,最近由于改造一个网关服务,用到了IdentityServer4的授权,改造过程中发现比较适合基于Role角 ...

  9. 在ASP.NET Core中构建路由的5种方法

    原文链接 :https://stormpath.com/blog/routing-in-asp-net-core 在ASP.NET Core中构建路由的5种方法 原文链接 :https://storm ...

随机推荐

  1. linq 多条件join

    var query=from a in db.A           join b in db.B.Where(c=>c.num>3)             on new {a.type ...

  2. javascript中的定时器

    本文地址:[http://www.xiabingbao.com/javascript/2015/04/20/javascript-timer/] 在以前的文章[javascript中的定时器]中,简单 ...

  3. key中断

    1 中断,很短的时间过去,然后回来.2 信号,软中断,而中断属于硬中断.3 实时内核,和分时内核.4 同步,预先知道发生,异步,预先不知道要发生,中断属于异步.5 arm一次执行一个中断.6 irq中 ...

  4. 为什么使用Lambda表达式(翻译版)

    简介 如上图所示,绿色框中就是Lambda表达式,是可以执行的代码块.Lambda表达式是很多编程语言的特征,比如Lisp, Python, Scala等. 但是对于java,在8以后才支持这种写法. ...

  5. ASP.NET 4.5 MVC 4 无法运行在Windows2008的IIS7.0上显示404的解决方案

    需要在web.config下加上这个 <system.webServer> <modules runAllManagedModulesForAllRequests="tru ...

  6. 【转】ftrace 简介

    ftrace 简介 ftrace 的作用是帮助开发人员了解 Linux 内核的运行时行为,以便进行故障调试或性能分析. 最早 ftrace 是一个 function tracer,仅能够记录内核的函数 ...

  7. leetcode 427. Construct Quad Tree

    We want to use quad trees to store an N x N boolean grid. Each cell in the grid can only be true or ...

  8. TStringHelper.Split

    作为对泛型的支持,TStringHelper.Split方法理所应当地出现了. 示例代码1: var  iText: string;  iAStr: TArray<string>;  I: ...

  9. LeetCode OJ:N-Queens(N皇后问题)

    Given an integer n, return all distinct solutions to the n-queens puzzle. Each solution contains a d ...

  10. certbot以standalone方式新建密钥

    下载:wget https://dl.eff.org/certbot-auto 授权:chmod a+x ./certbot-auto 快捷命令 ./certbot-auto certonly --t ...