.NET Nancy 详解(二) 简易路由实现
继续上面的简易版本,有意思的点剩下便是路由实现了。
路由注册
首先,来看一下基本的路由注册过程。
public FakeNancyModuleWithoutBasePath()
{
Delete["/"] = x => {
return "Default delete root";
};
Get["/"] = x => {
return "Default get root";
};
Get["/fake/should/have/conflicting/route/defined"] = x => {
return new Response { Contents = "FakeNancyModuleWithoutBasePath" };
};
Post["/"] = x => {
return "Default post root";
};
Put["/"] = x => {
return "Default put root";
};
}
这里的Get
,Post
,Put
,Delete
对应HttpMethod里面的4个方法。习惯了微软.Net MVC 或者.Net Webapi的人的可能初次使用会觉得比较怪,实际上这种方式在其他各种语言上都有类似的用法。
这里的实现只是4个类型为字典的属性,注册的过程实际上是字典里面赋值。
public IDictionary<string, Func<dynamic, Response>> Delete { get; private set; }
public IDictionary<string, Func<dynamic, Response>> Get { get; private set; }
public IDictionary<string, Func<dynamic, Response>> Post { get; private set; }
public IDictionary<string, Func<dynamic, Response>> Put { get; private set; }
当然,为了方便模块的划分,路由可以带统一的前缀,这里称为BasePath
public FakeNancyModuleWithBasePath() : base("/fake")
{
Delete["/"] = x => {
throw new NotImplementedException();
};
Get["/route/with/some/parts"] = x => {
return new Response { Contents = "FakeNancyModuleWithBasePath" };
};
}
路由解析
路由的解析由IRouteResolver
来完成,这里使用接口是为了方便将来实现不同的路由解析机制,以及单元测试。
public interface IRouteResolver
{
IRoute GetRoute(IRequest request, IEnumerable<RouteDescription> descriptions);
}
路由的解析核心就一句LINQ
,相当的简单的粗暴。
public IRoute GetRoute(IRequest request, IEnumerable<RouteDescription> descriptions)
{
var matchingRoutes =
from description in descriptions
let matcher = BuildRegexMatcher(description)
let result = matcher.Match(request.Path)
where result.Success
select new
{
Groups = result.Groups,
Description = description
};
var selected = matchingRoutes
.OrderByDescending(x => GetSegmentCount(x.Description))
.FirstOrDefault();
return selected != null ?
new Route(selected.Description.GetModuleQualifiedPath(), GetParameters(selected.Description, selected.Groups), selected.Description.Action) :
new NoMatchingRouteFoundRoute(request.Path);
}
下面来拆分这个过程。
descriptions是什么?
这个是我们刚开始注册的路由字典,当然预先已经依据请求的Http Verb预先过滤了一轮,这里的过滤方式采用反射request.Verb动词的方式,这个有改进的空间,但是无异于提供了一种实现的手段。我们的例子中,这一步就只留下了所有Get字典下面的路由规则。
public static IEnumerable<RouteDescription> GetRouteDescription(this NancyModule source, IRequest request)
{
const BindingFlags flags =
BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase;
var property =
typeof(NancyModule).GetProperty(request.Verb, flags);
if (property == null)
return Enumerable.Empty<RouteDescription>();
return
from route in property.GetValue(source, null) as IDictionary<string, Func<object, Response>>
select new RouteDescription
{
Action = route.Value,
ModulePath = source.ModulePath,
Path = route.Key
};
}
let
是LINQ 里面子查询的一个简易语法,这里首先使用嵌套语句遍历每个路由规则构建正则表达式。
对于不带参数的路由规则,正则表达就构建只是简单的忽视大小写。
对于带参数的路由规则,需要提取值。关于正则表达式的基础知识,参见其他博客。
[p2]
当然,规则很有可能有冲突,这里按照一定的规则返回第一条。
var selected = matchingRoutes
.OrderByDescending(x => GetSegmentCount(x.Description))
.FirstOrDefault();
当前版本的优先级规则是片段的数量
private static int GetSegmentCount(RouteDescription description)
{
var moduleQualifiedPath =
description.GetModuleQualifiedPath();
var indexOfFirstParameter =
moduleQualifiedPath.IndexOf('{');
if (indexOfFirstParameter > -1)
moduleQualifiedPath = moduleQualifiedPath.Substring(0, indexOfFirstParameter);
return moduleQualifiedPath.Split('/').Count();
}
当前匹配的路由规则"/fake/should/have/conflicting/route/defined"
的片段数就是7,当片段数也无法区别的时候就返回其中一条。
最后返回我们需要的Route
对象。
public Route(string path, RouteParameters parameters, Func<object, Response> action)
{
if (path == null)
{
throw new ArgumentNullException("path", "The path parameter cannot be null.");
}
if (action == null)
{
throw new ArgumentNullException("action", "The action parameter cannot be null.");
}
this.Path = path;
this.Parameters = parameters;
this.Action = action;
}
Route
里面的对象就是我们路由规则里面注册的方法。
.NET Nancy 详解(二) 简易路由实现的更多相关文章
- XSD详解二 - 简易元素、属性、内容限定
一.XSD 简易元素 XML Schema 可定义 XML 文件的元素. 简易元素指那些只包含文本的元素.它不会包含任何其他的元素或属性. 1.什么是简易元素? 简易元素指那些仅包含文本的元素.它不会 ...
- pika详解(二) BlockingConnection
pika详解(二) BlockingConnection 本文链接:https://blog.csdn.net/comprel/article/details/94592348 版权 Blocki ...
- .NET DLL 保护措施详解(二)关于性能的测试
先说结果: 加了缓存的结果与C#原生代码差异不大了 我对三种方式进行了测试: 第一种,每次调用均动态编译 第二种,缓存编译好的对象 第三种,直接调用原生C#代码 .net dll保护系列 ------ ...
- PopUpWindow使用详解(二)——进阶及答疑
相关文章:1.<PopUpWindow使用详解(一)——基本使用>2.<PopUpWindow使用详解(二)——进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...
- Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)
[Android布局学习系列] 1.Android 布局学习之——Layout(布局)详解一 2.Android 布局学习之——Layout(布局)详解二(常见布局和布局参数) 3.And ...
- logback -- 配置详解 -- 二 -- <appender>
附: logback.xml实例 logback -- 配置详解 -- 一 -- <configuration>及子节点 logback -- 配置详解 -- 二 -- <appen ...
- 爬虫入门之urllib库详解(二)
爬虫入门之urllib库详解(二) 1 urllib模块 urllib模块是一个运用于URL的包 urllib.request用于访问和读取URLS urllib.error包括了所有urllib.r ...
- [转]文件IO详解(二)---文件描述符(fd)和inode号的关系
原文:https://www.cnblogs.com/frank-yxs/p/5925563.html 文件IO详解(二)---文件描述符(fd)和inode号的关系 ---------------- ...
- Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)
View 的绘制系列文章: Android View 的绘制流程之 Measure 过程详解 (一) Android View 绘制流程之 DecorView 与 ViewRootImpl 在上一篇 ...
随机推荐
- apache2 + virtualenv +djangocms
命令记录: cd /var/www makedir django cd django/ virtualenv env --no-site-packages source /var/www/django ...
- pycharm3.4 下svn 项目checkout&配置
pycharm 社区版: 3.4 1. checkout 项目 注意,之前配置好:设置里面的一些配置:(以下勾勾不要勾上) 2. checkout 项目之后,做以下操作: vcs ->enabl ...
- 如何解决phpcms后台验证码不显示的问题
方法一: 主要在于是否开启gd库 查看办法 找到php.ini文件 搜索extension=php_gd2.dll这段代码(windows) 然后把前面的;符号去掉即可. centOS6.5中可能需要 ...
- Scala教程
Scala表示可扩展性语言,是一种混合函数式编程语言.它是由Martin Odersky创建,并于2003年首次发布. Scala平滑地集成面向对象和函数式语言的特点,并且Scala被编译在Java虚 ...
- Tomcat 7最大并发连接数的正确修改方法
这是个很简单的问题,但是搜了一圈,发现大家都写错了.所以这里总结一下: 几乎所有的中文网页都介绍,要修改Tomcat的默认最大并发连接数,应该进行如下设置(实际上这些步骤是错误的): -------- ...
- 基础01 dos命令
常见的dos命令: 盘符: 进入指定的盘下面. 操作文件夹: dir 列出当前控制 ...
- percona-toolkit 之 【pt-slave-delay】说明
摘要: 在主从复制的架构中,正常情况下主上操作的记录也会在从上进行操作,虽说是异步复制,但操作会“实时”的同步到从.比如在主上不小心误操作了,还没等反应过来从上也会马上执行误操作,后期只有通过二进制或 ...
- Java中的Comparable接口和Comparator接口
Comparator位于包java.util下,比较器,是在集合外部定义排序.Comparable位于包java.lang下,代表当前对象可比较的,是在集合内部实现排序. Comparable代表一个 ...
- Effective C++ -----条款08: 别让异常逃离析构函数
析构函数绝对不要吐出异常.如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序. 如果客户需要对某个操作函数运行期间抛出的异常作出反应,那么class应 ...
- UVALive 4953 Wormly--【提醒自己看题要仔细】
有一条虫要过桥,桥有断的木板和好的木板.虫有L条腿B个身体,桥长N.01串表示桥的好坏.输出最少操作次数. 一开始虫的身体在最左B桥上,腿在最左L桥上,最后要到达最右B以及最右L... 操作有2种:① ...