ASP.NET Core的路由[4]:来认识一下实现路由的RouterMiddleware中间件
虽然ASP.NET Core应用的路由是通过RouterMiddleware这个中间件来完成的,但是具体的路由解析功能都落在指定的Router对象上,不过我们依然有必要以代码实现的角度来介绍一下这个中间件。在这之前,我们先来认识一个特殊的特性。[本文已经同步到《ASP.NET Core框架揭秘》之中]
让RouterMiddleware中间件委托Router完整整个路由工作之后,解析出来的路由参数会以一个RouteData对象的形式存储在RouteContext上下文中。但是RouteContext是为Router的执行建立的上下文,路由解析工作完成之后,这个上下文的生命周期也随着结束,既然整个RouteContext上下文都不存在了,请求处理的后续步骤如何获取这个RouteData对象呢?
通过《注册URL模式与HttpHandler的映射关系》的实例演示我们知道可以调用HttpContext的扩展方法GetRouteData来获取这个包含素所有路由参数的RouteData对象,这个意味着原本依附于RouteContext上下文的RouteData最终会被附加到代表当前请求上下文的HttpContext上,而具体承载这个RouteData的就是这个名为RoutingFeature的特性。RoutingFeature是我们对所有实现了IRoutingFeature接口的所有类型以及对应对象的统称。如下面的代码片段所示,这个接口通过属性RouteData来保存最终附加到HttpContext的RouteData。RoutingFeature类是这个接口的默认实现者,我们的RouterMiddleware默认情况下就是使用这个对象。
1: public interface IRoutingFeature 2: { 3: RouteData RouteData { get; set; } 4: } 5: 6: public class RoutingFeature : IRoutingFeature 7: { 8: public RouteData RouteData { get; set; } 9: }
如下所示的代码片段体现了RouterMiddleware处理请求的完整逻辑。我们在创建一个RouterMiddleware对象的时候需要指定一个Router对象,以及一个用来创建Logger的LoggerFactory。当这个中间件开始处理请求的时候,它会根据当前HttpContext创建一个RouteContext上下文对象,并将其作为参数调用Router的RotueAsync方法进行路由解析。如果在路由解析结束之后通过RouteContext的Handler属性返回的请求处理存在,意味着当前请求与注册的路由匹配,在此情况下它会将当前请求交给这个处理器做后续处理。在这之前它会从RouteContext上下文中提出出RouteData,然后据此创建一个RoutingFeature对象并附加到HttpContext上面。
1: public class RouterMiddleware 2: { 3: private ILogger _logger; 4: private RequestDelegate _next; 5: private IRouter _router; 6: 7: public RouterMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IRouter router) 8: { 9: _next = next; 10: _logger = loggerFactory.CreateLogger<RouterMiddleware>(); 11: _router = router; 12: } 13: 14: public async Task Invoke(HttpContext context) 15: { 16: RouteContext routeContext = new RouteContext(context); 17: routeContext.RouteData.Routers.Add(_router); 18: await _router.RouteAsync(routeContext); 19: if (null == routeContext.Handler) 20: { 21: _logger.LogDebug(1, "Request did not match any routes."); 22: await _next(context); 23: } 24: else 25: { 26: context.Features.Set<IRoutingFeature>(new RoutingFeature { RouteData = routeContext.RouteData}) 27: await routeContext.Handler(context); 28: } 29: } 30: }
我们除了可以调用HttpContext的扩展方法GetRouteData得到封装了路由参数的RouteData对象之前,我们还可以调用另一个名为GetRouteValue发的扩展方法直接获取某个路由参数的值。在如下所示的代码片段中,我们采用比较简单代码展示了这两个扩展放的实现。
1: public static class RoutingHttpContextExtensions 2: { 3: public static RouteData GetRouteData(this HttpContext context) 4: { 5: return context.Features.Get<IRoutingFeature>()?.RouteData; 6: } 7: 8: public static object GetRouteValue(this HttpContext context, string key) 9: { 10: return context.GetRouteData()?.Values[key]; 11: } 12: }
一般来说我们倾向于调用ApplicationBuilder的扩展方法UseRouter来注册RouterMiddleware中间件。具体来说,我们可以选择如下两个UseRouter方法重载。如果调用第一个重载,我们需要为注册的RouterMiddleware中间件提供一个具体的Router对象。对于第二个重载来说,这个Router对象实际上是利用RouteBuilder创建的,我们在调用这个方法的时候需要以Action<IRouteBuilder>对象的形式利用这个RouteBuilder注册所需的路由。
1: public static class RoutingBuilderExtensions 2: { 3: public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router) 4: { 5: return builder.UseMiddleware<RouterMiddleware>(new object[] { router }); 6: } 7: 8: public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, Action<IRouteBuilder> action) 9: { 10: RouteBuilder routeBuilder = new RouteBuilder(builder); 11: action(routeBuilder); 12: return builder.UseRouter(routeBuilder.Build()); 13: } 14: }
- 高性能网站架构方案(二)——优化网站响应时间
- VFS四大对象之二 struct inode
- Ubuntu 安装 Redis
- 高性能网站架构方案(三) ——Varnish加速与Gearman任务分发
- mysql 性能优化方案 (转)
- 《Redis设计与实现》读书笔记(一)——简单动态字符串(SDS)
- Comet:基于 HTTP 长连接的“服务器推”技术
- 编码修炼 | 快速了解Scala技术栈
- VFS四大对象之三 struct dentry
- PHP 排序算法实现讲解
- PHP7新特性介绍
- VFS四大对象之四-struct file
- 《Redis设计与实现》读书笔记(二) ——Redis中的字典(Hash)
- 《Redis设计与实现》读书笔记(三) ——Redis中的链表
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android开发中自定义ProgressBar控件的方法示例
- Android 使用自定义RecyclerView控件实现Gallery效果
- Android开发中GridView用法示例
- React Native中Android物理back键按两次返回键即退出应用
- Android仿简书搜索框效果的示例代码
- Android 自定义 HorizontalScrollView 打造多图片OOM 的横向滑动效果(实例代码)
- Android开发自定义TextView省略号样式的方法
- Android开发中获取View视图宽与高的常用方法小结
- Android判断字符串中是否含字母、中文或数字
- 利用Kotlin开发你的第一个Android应用
- Android Picasso使用高斯模糊处理的示例代码
- Android RecyclerView打造悬浮效果的实现代码
- Android选择图片或视频进行循环播放
- Android第三方登录之QQ登录
- Android利用CountDownTimer实现验证码倒计时效果实例