自定义路由匹配和生成

前言

前两篇文章主要总结了CMS系统两个技术点在ASP.NET Core中的应用:

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

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

而本篇文章,继续介绍另一个技术点:自定义路由匹配和生成。

背景

在MVC5时代,默认的路由可能就是简单的约定/{controller}/{action}/{id},第一节对应控制器(Controller)名,第二节对应操作(Action)名,第三节是参数名。
在WebApi和ASP.NET Core时代,有了Route特性来指定相应操作的路由指向,可以很灵活地配置RESTful Api。

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

  • 栏目的列表 ->/{父栏目名}/{子栏目名}-{页码}/

  • 文章详情页 ->/{栏目名}/{文章名}.html

  • 标签页 ->/{标签名}

这些友好的url链接使用默认的路由约定是很难实现的,当然是可以配置路由规则去传递参数:

  1. app.UseMvc(routes =>

  2. {

  3.    routes.MapRoute(

  4.        name: "article_list",

  5.        template: "{parentCategory}/{category}-{page}/",

  6.        defaults: new { controller = "Article", action = "Index" });

  7.    routes.MapRoute(

  8.        name: "article_detail",

  9.        template: "{category}/{article}.html",

  10.        defaults: new { controller = "Article", action = "Detail" });

  11.    routes.MapRoute(

  12.        name: "tags",

  13.        template: "{tag}/",

  14.        defaults: new { controller = "Article", action = "Tag" });

  15.    });

但是,这样配置很繁琐,也不灵活,如果还要传入更多规则,比如不向下匹配,就显得捉襟见肘了。那有没有更灵活的方案呢?当然有,就是本文接下来要介绍的IRouter接口。

原理

上一节最后介绍的一般路由配置方法,其实是MapRoute方法创建一个个IRouter的实例,添加到IRouteBuilder实例中,具体方法可以看看源码。

所以我们可以实现一个自定义的IRouter,就能不用默认的约定,去实现我们的苛刻需求。

首先要看看IRouter这个接口的定义,源码在aspnet/Routing/src/Microsoft.AspNetCore.Routing.Abstractions/IRouter.cs

  1. namespace Microsoft.AspNetCore.Routing

  2. {

  3.    public interface IRouter

  4.    {

  5.        Task RouteAsync(RouteContext context);

  6.        VirtualPathData GetVirtualPath(VirtualPathContext context);

  7.    }

  8. }

实现

IRouter接口只有两个方法,本文分别给出代码示例来介绍。

RouteAsync

这个方法中实现路由匹配,从路由上下文中获取所需要的数据,存入RouteData字典中,再从控制器取出做相应的处理。

  1. public async Task RouteAsync(RouteContext context)

  2. {

  3.    var requestedUrl = context.HttpContext.Request.Path.Value.TrimStart('/').ToLower();

  4.    var split = requestedUrl.Split('/');

  5.    if (secoend != null && secoend.EndsWith(".html") && split.Length == 2)

  6.    {

  7.        var title = secoend.Replace(".html", "");

  8.        context.RouteData.Values["controller"] = "Article";

  9.        context.RouteData.Values["action"] = "Detail";

  10.        context.RouteData.Values["category"] = first;

  11.        context.RouteData.Values["title"] = title;

  12.    }

  13.    //...对请求路径进行一系列的判断

  14.    //最后注入`MvcRouteHandler`示例执行`RouteAsync`方法,表示匹配成功

  15.    await context.HttpContext.RequestServices.GetService<MvcRouteHandler>().RouteAsync(context);

  16. }

这样,当请求路由为/news/asp.net.html时,就会匹配到上面的规则,请求进入Article控制器中的Detail操作被处理,并且可以从控制器中的RouteData.Values["category"]?.ToString();方法拿到所需的数据。

GetVirtualPath

这个方法实现路由生成,可以从路由上下文中获取RouteData字典中的数据,进行虚拟路径(区别与真实目录)的生成。

  1. public VirtualPathData GetVirtualPath(VirtualPathContext context)

  2. {

  3.    var path = string.Empty;

  4.    var hasController = context.Values.TryGetValue("controller", out var controller);

  5.    var hasAction = context.Values.TryGetValue("action", out var action);

  6.    var hasCategory = context.Values.TryGetValue("category", out var category);

  7.    var hasTitle = context.Values.TryGetValue("title", out var title);

  8.    if (hasController && hasAction && hasCategory && hasTitle)

  9.    {

  10.        path = $"/{category/{title}.html";

  11.    }

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

  13. }

这样,当调用路径生成方法@Url.Action("Detail","Article",new { title="asp.net", category="news" }),就会生成”/news/asp.net.html”这样的路径来。

IRouter的设置生效

自定义实现的IRouter如何设置到原有的MVC项目中呢?方法很简单,上面已经简单说道了,其实app.UseMvc这个方法就有添加IRouter的方法:

  1. app.UseMvc(routes =>

  2. {

  3.    //添加 自定义路由匹配与url生成组件

  4.    routes.Routes.Add(new RouteProvider());

  5. });

这里RouteProviderIRouter的实现,这样自定义的路由提供对象就生效啦!

相关小技巧

  1. SEO中会有一条铁规则,就是不是有效链接的情况下只能返回404,比如,手动输入了一个路径,能匹配到文章详情的操作(action),如果数据库中查不出文章,本来应该不能匹配的,但是,如果在匹配的时候查询数据库确认是否存在的话,会加大系统的压力,所以,可以放在操作(action)中查询,查不到再返回NotFound,让上一篇文章《ASP.NET Core 中的SEO优化(2):中间件中渲染Razor视图》中介绍的中间件一并处理输出404页面。

  2. 站内链接如果使用带域名的绝对路径,能够提高优化效果,我们可以自己写一个UrlHelper的扩展方法:

  1. public static class UrlHelperExtensions

  2. {

  3.    public static string AbsoluteAction(

  4.        this IUrlHelper helper,

  5.        string actionName,

  6.        string controllerName,

  7.        object routeValues = null)

  8.    {

  9.        string scheme = helper.ActionContext.HttpContext.Request.Scheme;

  10.        return helper.Action(actionName, controllerName, routeValues, scheme);

  11.    }

  12.    public static string AbsoluteContent(

  13.        this IUrlHelper helper,

  14.        string contentPath)

  15.    {

  16.        return new Uri(helper.ActionContext.HttpContext.Request.GetUri(), helper.Content(contentPath)).ToString();

  17.    }

  18.    public static string AbsoluteRouteUrl(

  19.        this IUrlHelper helper,

  20.        string routeName,

  21.        object routeValues = null)

  22.    {

  23.        string scheme = helper.ActionContext.HttpContext.Request.Scheme;

  24.        return helper.RouteUrl(routeName, routeValues, scheme);

  25.    }

  26. }

总结

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

原文:https://yangshunjie.com/A-Middleware-Implement-For-Customized-Routing-In-AspNetCore.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322746.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法

转载自 mybatis多个参数(不使用param注解情况下)&#xff0c;sql参数占位符正确写法 useActualParamName配置 useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性&#xff0c;你的工程必须采用Java 8编译&#xff0c;并且加上-parameters选项。&…

如何封装并发布一个属于自己的ui组件库

以前就一直有个想法自己能不能封装一个类似于elementui一样的组件库&#xff0c;然后发布到npm上去&#xff0c;毕竟前端说白了&#xff0c;将组件v上去&#xff0c;然后进行数据交互。借助这次端午&#xff0c;终于有机会&#xff0c;尝试自己去封装发布组件库了 我这里了只做…

P1768-天路【负环,SPFA,01分数规划,二分答案】

正题 题目链接:https://www.luogu.org/problemnew/show/P1768 题目大意 求一条回路使得路上∑vi∑pi\frac{\sum v_i}{\sum p_i}∑pi​∑vi​​最大。 解题思路 考虑01分数规划 ∑vi∑pians\frac{\sum v_i}{\sum p_i}ans∑pi​∑vi​​ans ∑vians∗∑pi\sum v_ians*\sum p_i…

听云支持.NET Core的应用性能监控

随着微软于2017年8月正式发布.NET Core 2.0&#xff0c; .NET Core 社区开始活跃&#xff0c;众多.NET开发者开始向跨平台转变。 听云于2017年11月推出了.NET Core应用监控工具&#xff0c;和听云其他语言的监控工具一样&#xff0c;.NET Core应用监控工具具有以下特征&#xf…

mybatis源码阅读(五) ---执行器Executor

转载自 mybatis源码阅读(五) ---执行器Executor 1. Executor接口设计与类结构图 public interface Executor {ResultHandler NO_RESULT_HANDLER null;// 执行update&#xff0c;delete&#xff0c;insert三种类型的sql语句int update(MappedStatement ms, Object parameter…

.sync的一个用法

面试时&#xff0c;有人问了我修饰符是什么&#xff0c;就是一个点后面加一个单词&#xff0c;我当时还以为是什么文件夹后缀呢。很是尴尬 这里主要学习下.sync的一个用法 假设下场景&#xff1a; 这里有一个父组件&#xff0c;父组件中有个money&#xff0c;需要传到子组件中…

nssl1296-猫咪的进化【dp】

正题 题目大意 nnn次&#xff0c;每次有3种选择&#xff1a; 休息获得viv_ivi​点价值获得vi2v_i^2vi2​点价值且下一回合要休息 解题思路 定义fi,0/1/2f_{i,0/1/2}fi,0/1/2​表示第iii次为休息/叫一声/叫两声时的最大价值。 fi,0f_{i,0}fi,0​可以由前面任何状态转移过来。 …

[52ABP实战系列] .NET CORE实战入门第三章更新了

早安 各位道友好&#xff0c;.NET CORE入门视频的第三章也算录制完毕了。欢迎大家上传课网进行学习。 更新速度 大家也知道最近的社会新闻比较多。频繁发生404、关键字打不出来&#xff0c;我个人也在关注这些事件。导致精力分散&#xff0c;没有做到稳定更新&#xff0c;现在呢…

如何安装nuxt

因为vue是单页面应用&#xff0c;所以不被Seo&#xff0c;如百度和Google抓取到&#xff0c;在Vue中如果想要爬虫爬到就必须使用nuxt 那么如何安装使用呢&#xff1f; yarn create nuxt-app <project-name> cd <project-name> yarn build yarn start必须先build&a…

mybatis源码阅读(六) ---StatementHandler了解一下

转载自 mybatis源码阅读(六) ---StatementHandler了解一下 StatementHandler类结构图与接口设计 BaseStatementHandler&#xff1a;一个抽象类&#xff0c;只是实现了一些不涉及具体操作的方法 RoutingStatementHandler&#xff1a;类似路由器&#xff0c;根据配置文件来路由…

nssl1298-网站计划【线段树】

正题 题目大意 若干个区间操作l,rl,rl,r 让答案增加(lr)∗max{ai}(i∈[l..r])(lr)*max\{a_i\}(i\in[l..r])(lr)∗max{ai​}(i∈[l..r]) 并把最大的数变为0 解题思路 线段树&#xff0c;上传最大值时多上传一个位置&#xff0c;然后单点修改。 codecodecode #include<cstd…

输入框限定保留三位小数点

这里用到正则表达式&#xff0c;没输入一个数字会对输入框进行一次事件的触发&#xff0c;检查是否超过三位小数点&#xff0c;超过则进行删除。 你可以3改成2&#xff0c;这样就是保留两位小数点了 <el-input placeholder"请输入商品重量" v-model"baseInfo…

基于OIDC(OpenID Connect)的SSO(纯JS客户端)

在上一篇基于OIDC的SSO的中涉及到了4个Web站点&#xff1a; oidc-server.dev&#xff1a;利用oidc实现的统一认证和授权中心&#xff0c;SSO站点。 oidc-client-hybrid.dev&#xff1a;oidc的一个客户端&#xff0c;采用hybrid模式。 oidc-client-implicit.dev&#xff1a;od…

mybatis源码阅读(七) ---ResultSetHandler了解一下

转载自 mybatis源码阅读(七) ---ResultSetHandler了解一下 1、MetaObject MetaObject用于反射创建对象、反射从对象中获取属性值、反射给对象设置属性值&#xff0c;参数设置和结果封装&#xff0c;用的都是这个MetaObject提供的功能。 public static MetaObject forObject…

nssl1299-选做作业【最大流,最小割,最大子权闭合图】

正题 题目大意 有nnn个任务&#xff0c;完成需要先决条件&#xff0c;然后有完成价值。选择一些任务完成&#xff0c;求最大价值。 解题思路 首先&#xff0c;如果有环&#xff0c;那么这些环是不可能完成的。所以先用拓扑排序找环。 然后考虑最大子权闭合图&#xff0c;对于…

IIS中的 Asp.Net Core 和 dotnet watch

在基于传统的.NET Framework的Asp.Net Mvc的时候&#xff0c;本地开发环境中可以在IIS中建立一个站点&#xff0c;可以直接把站点的目录指向asp.net mvc的项目的根目录。然后build一下就可以在浏览器里面刷新到最新的修改了&#xff0c;也可以附加到w3wp的进程进行调试。但是在…

mybatis源码阅读(八) ---Interceptor了解一下

转载自 mybatis源码阅读(八) ---Interceptor了解一下 1 Intercetor MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下&#xff0c;MyBatis允许使用插件来拦截的方法调用包括&#xff1a; Executor (update, query, flushStatements, commit, rollba…

foreach方法使用

用法 foreach方法主要是针对数组而言的&#xff0c;对数组中的每个元素可以执行一次方法 var array [a, b, c, e]; array.forEach((a)> {console.log(a); });属性 foreach方法主要有三个参数&#xff0c;分别是数组内容、数组索引、整个数组 var array [a, b, c, e]; arra…

ssl提高组周六模拟赛【2019.3.2】

前言 Rank1Rank1Rank1耶 成绩 RankRankRank是有算别人的 只放前Rank10Rank10Rank10 RankRankRankPersonPersonPersonScoreScoreScoreAAABBBCCCDDD111meselfmeselfmeself340340340808080100100100100100100606060222XXYXXYXXY250250250808080707070100100100000333LWLWLW2502502…

.NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions

.NET Core 控制台程序没有 ASP.NET Core 的 IWebHostBuilder 与 Startup.cs &#xff0c;那要读 appsettings.json、注依赖、配日志、设 IOptions 该怎么办呢&#xff1f;因为这些操作与 ASP.NET Core 无依赖&#xff0c;所以可以自己动手&#xff0c;轻松搞定。 1、读 appsett…