在上一篇中简单讲了一些基础知识,例如Asp.Net Core Middleware 的使用,DI的简单使用以及嵌入式资源的使用方法等。本篇就是结合基础知识来构建一个基础框架出来。git
那么框架有什么功能呢?github
下面就基于以上四点搭建基础框架。其余缓存,日志什么的就先不在介绍。缓存
正如上一篇介绍的那样,实现一个中间件就能够作拦截请求操做,换句话说,若是是layim的请求,咱们不要放过。若是不是,那么拜拜。可是因为咱们又使用了系统的 EmbeddedFileProvider ,因此静态资源交给系统去处理就好。这里呢我使用一个很简单的方式来判断是不是LayIM的请求,就是经过请求的path前缀去判断。在 LayIMMiddleware入口方法Invoke中,经过IsLayIMRequest扩展方法去判断是不是LayIM请求。代码以下:app
/// <summary> /// 是否LayIM接口请求 /// </summary> /// <param name="context"></param> /// <param name="options"></param> /// <returns></returns> public static bool IsLayIMRequest(this HttpContext context, LayIMOptions options) { return IsConfigPath(context.Request.Path.Value) || context.Request.Path.Value.StartsWith(options.ApiPrefix, StringComparison.CurrentCultureIgnoreCase); }
没错,就这么简单粗暴,用了一个StartWith方法。代码中IsConfigPath之后在讲。在这里,前缀能够是用户自定义的。能够在UselayIM中传入定义方法:框架
app.UseLayIM(options => { options.ApiPrefix = "/mylayim"; });
好比上文中我改为了/mylayim开头的,测试一下。ide
能够看到,正常处理。函数
正如上文中的路径 /mylayim/init?uid=1 是如何进行处理的呢?这里咱们的路由就要出场了。以前这段代码仍是借鉴了Hangfire中 的代码实现的。它的路由很简单,就是经过正则去匹配。不过我这里实现的路由没有那么强大,为了方便,不少url都定义死了。并且不支持url中带参数解析的状况,例如 init/{uid}.不过这个后期会考虑。路由匹配代码以下:测试
/// <summary> /// 经过path找到对应的Dispatcher /// </summary> /// <param name="path"></param> /// <returns></returns> private Tuple<ILayIMDispatcher, Match> FindDispatcherMatch(string path) { if (string.IsNullOrEmpty(path)) { path = "/"; } foreach (var dispatcher in dispatchers) { var pattern = $"^{dispatcher.Item1}$" ; var match = Regex.Match(path, pattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); if (match.Success) { return new Tuple<ILayIMDispatcher, Match>(dispatcher.Item2, match); } } return null; }
没错就这么一个方法实现了路由,是否是很简单。(复杂的还没去研究。。。。)从代码中咱们能够看到方法返回了一个 Tuple<ILayIMDispatcher, Match> ,这个ILayIMDispatcher是何方神圣呢?让咱们进入下一节吧。ui
这个调度器翻译的不是很准确,不过你们理解就好。若是不理解的话,看下面的图就知道了。this
接口ILayIMDispatcher里面就一个方法
Task Dispatch(HttpContext context);
那么他们又能够细分为多种类型,在CQRS的概念里,咱们对聚合的增删改都属于命令(Command),那么咱们能够定义一个CommandDispatcher,不过我这里没有那么严格按照CQRS的方式,因此查询我也把他归类为查询命令:QueryCommandDispatcher。
下面咱们看一下具体代码:
internal class QueryCommandDispatcher<TResult> : CommandDispatcher<TResult> { protected override string AllowMethod => HttpGet; private readonly Func<HttpContext, TResult> executeFunction; public QueryCommandDispatcher(Func<HttpContext, TResult> executeFunction) { this.executeFunction = executeFunction; } }
在构造函数里面咱们传入了一个 Func<HttpContext, TResult>,那么这个Func就是咱们的业务逻辑了。
好比在路由里,咱们添加 /layim/init 的 QueryCommandDispatcher。代码以下:
//layim初始化接口 routes.AddQueryCommand<object>("/init", context => { //这里只是演示(逻辑未实现) return context.Request.Query["uid"]; });
其中AddQueryCommand是路由的一个扩展方法:
/// <summary> /// 注册返回值为TResult类型的命令路由 /// </summary> /// <typeparam name="TResult">返回类型</typeparam> /// <param name="routes">当前路有集合</param> /// <param name="path">路径</param> /// <param name="command">执行命令</param> public static void AddQueryCommand<TResult>(this RoutesCollection routes, string path, Func<HttpContext, TResult> command) { Error.ThrowIfNull(path, nameof(path)); Error.ThrowIfNull(command, nameof(command)); routes.Add(path, new QueryCommandDispatcher<TResult>(command)); }
那么,这样的话,路由第一步先找到相对应 /layim/init 的调度器,而后执行Dispatch方法便可,最后返回所须要的数据。正如第一节里的截图那个最终处理效果。
通用接口其实在以前的文章中有讲过,他的做用就是业务和框架解耦。也就是说我设计好一个通用接口,若是用户不想使用框架的默认实现,能够自行定义实现方法,而后经过依赖注入的形式替换掉框架默认实现,这里不在赘述。好比框架的默认实现是Dapper,那么用户能够本身改成EntityFramework或者其余实现。
本文简单的介绍了框架的结构和基本实现,实现较为简单,功能相对来讲比较单一,不过因为是偏向LayIM业务的,因此并无想把它设计的多么复杂,功能多么强大,并且主要是能力不够,哈哈哈哈。
博客预告:LayIM.AspNetCore Middleware 开发日记(四)主角登场(LayIM介绍)
项目地址:https://github.com/fanpan26/LayIM.AspNetCore (本文代码对应blog3分支或者直接查看master)欢迎小伙伴们star 围观 提意见。