LayIM.NetClient 组件开发记录

前言

  好久没写博客了。前阶段看了下Hangfire组件,后来对其代码比较感兴趣,当时不太了解他如何生成的页面和一些访问请求等。后来看了下源代码,发现原来是 OWIN 在搞怪。于是乎开始深入研究Hangfire源代码,当然只是Route和Razor部分,具体他的核心业务后台执行任务我没研究。因为,我想到了之前做的LayIM的一个对接。那时候写的代码和项目耦合度太高,于是冒出一个写LayIM Owin插件的想法。直接开工吧。

开工

  主要参考了Hangfire的Dashboard部分。https://github.com/HangfireIO/Hangfire/tree/master/src/Hangfire.Core/Dashboard

  与之前的代码相比,这些代码都没有放入WebUI 端中,而且,分层也比较少。就一个 Owin中间件和SqlServer的实现。代码结构如下:(我自己当然写不出啦,参考上文链接中的代码)

  

  用过OWIN的都知道,就是通过IAppBuilder接口注册中间件。其实还是挺复杂的,虽然照着写了出来。为了避免误导观众,我就不多解释了。不过路由那一块我我可以说一下我的理解。

  首先定义路由集合,集合中包含所有注册的路径和实现 ILayIMDispatcher 接口的 Dispatcher。

  

  public class RouteCollection
    {
        private readonly List<Tuple<string, ILayimDispatcher>> _dispatchers = new List<Tuple<string, ILayimDispatcher>>();

        /// <summary>
        /// 注册路由
        /// </summary>
        /// <param name="pathTemplate">路由地址</param>
        /// <param name="dispatcher">处理方法</param>
        public void Add(string pathTemplate, ILayimDispatcher dispatcher)
        {
            Error.ThrowIfNull(pathTemplate, nameof(pathTemplate));
            Error.ThrowIfNull(dispatcher, nameof(dispatcher));

            _dispatchers.Add(new Tuple<string, ILayimDispatcher>(pathTemplate, dispatcher));
        }

        /// <summary>
        /// 根据Path寻找对应的Dispatcher
        /// 通过正则表达式来找到匹配的结果
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns></returns>
        public Tuple<ILayimDispatcher, Match> FindDispatcher(string path)
        {
            if (path.Length == 0) path = "/";

            foreach (var dispatcher in _dispatchers)
            {
                var pattern = dispatcher.Item1;

                if (!pattern.StartsWith("^", StringComparison.OrdinalIgnoreCase)) {
                    pattern = $"^{pattern}";
                }

                if (!pattern.EndsWith("$", StringComparison.OrdinalIgnoreCase)) {
                    pattern += "$";
                }

                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;
        }
    }

  请求,进入中间件的处理代码:

                var owinContext = new OwinContext(env);
                var context = new OwinLayimContext(storage,options, env);

                var path =  owinContext.Request.Path.Value;

                //匹配路由
                var findResult = routes.FindDispatcher(path);
                //如果没有匹配到,执行下一个
                if (findResult == null) {
                    return next(env);
                }

                //匹配成功之后执行 Dispatch
                context.UriMatch = findResult.Item2;
                //执行具体disptach方法,返回相应结果
                return findResult.Item1.Dispatch(context);

  举例来说:Layim中的初始化接口。定义为 /init  需要参数为 id. 那么在路由中,注册如下:

       //AddQuery只是又封装了一层,内部还是调用了Add方法。
       Routes.AddQuery<long>("/init", "id", (context, uid) =>
             {
                  //返回处理结果
             });

  在上边的例子中,AddQuery方法,注册了一个 SingleParameterQueryDispatcher<T> 的处理类。 他的作用就是处理拥有单个参数的类型为T的请求。具体代码如下:

        public async Task Dispatch(LayimContext context)
        {
            var request = context.Request;
            var response = context.Response;

            var parameterValue = request.GetQuery(_parameterName);
            //如果不是Get请求,返回方法不允许
            if (!CommandMethod.Equals(request.Method, StringComparison.OrdinalIgnoreCase))
            {
                response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                await Task.FromResult(false);
            }
            //返回结果为"application/json";
            context.Response.ContentType = "application/json";
            //将参数转化为相应的类型,有 null 异常
            T value = (T)Convert.ChangeType(parameterValue, typeof(T));
            //执行具体处理函数
            var result = _command(context, value);
            //序列化结果
            var json = context.Options.Serializer.SerializeObject(result);
            //返回
            await context.Response.WriteAsync(json);
        }

总结

  简单了介绍一下路由部分的内容,相信很多小伙伴也是看的云里雾里的。看懂OWIN机制,就能够懂了。我也是大部分借鉴(抄袭)了Hangfire中的代码。

  基本思路:

  1. 注册路由
  2. 实现路由匹配方法
  3. 找到对应的处理器
  4. 处理方法,返回结果

  在不懂的话,建议直接阅读源代码,阅读源代码能有意想不到的收获。

附:LayIM.NetClient中间件的使用方法。其中IM通讯我使用了融云,所以在其中也做了一些封装。

  

 public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            //使用SQL Server
            GlobalConfiguration.Configuration.UseSqlServer("LayIM_Connection");

            //使用layim api 6tnym1brnmpt7
            app.UseLayimApi("/layim", new LayimOptions
            {
                RongCloudSetting = new RongCloudSetting()
            });
        }
    }

  LayIM中注册的路由如下:

  

  运行结果:

  

  

  获取融云token的方法:

  

  

  代码运行正常

  

  最后:我觉得Owin中间件真的很方便。首先能够避免web项目中写太多的重复的业务逻辑。(当然,layim部分抽出成api也可以)  通过封装成 LayIM.NetClient中间件以后,不管是webform还是mvc都可以通过Owin来适配。后边还会继续完善。今天就到此为止吧~~

时间: 2024-10-14 19:45:32

LayIM.NetClient 组件开发记录的相关文章

饿了么基于Vue2.0的通用组件开发之路(分享会记录)

Element:一套通用组件库的开发之路 Element 是由饿了么UED设计.饿了么大前端开发的一套基于 Vue 2.0 的桌面端组件库.今天我们要分享的就是开发 Element 的一些心得. 官网:http://element.eleme.io/#/github:https://github.com/ElemeFE/element ## 设计目的 大部分项目起源都是源于业务方的需求,Element 也是一样.随着公司业务发展,内部开始衍生出很多后台系统,UED 部门也接到越来越多的设计需求,

java 组件开发中的日志记录问题

今天帮别人写封装几个url 请求,打成jar 包,可以以java接口的方式提供给外部访问. 遇到两个问题: 1. 是否把依赖的jar包也 打入 我要生成的jar包中,如果你不打入,别人直接调用接口会报错.如果你打入可能会和使用该jar包的项目发生冲突,因为别人可能已经引入了相关jar包. 自己可以根据要求来定: 如果不打就需要把jar和pom文件都提供给需求提出者. 如果需要可以使用maven 插件 maven-assembly-plugin. <plugin> <groupId>

基于唯一状态的前端组件开发

facebook的react的框架提出了一个基于唯一状态来渲染前端组件的想法.什么是唯一状态,採用唯一状态渲染究竟有什么优点. 希望大家看到这篇文章以后不用不论什么框架也能够写出基于唯一状态渲染的前端组件. 基于唯一状态的组件的开发模式就是组件内部永远仅仅存在一份数据来表示组件的状态,而且更新组件时仅仅仅仅使用这一份数据. 这样的开发模式的优点.主要体如今以下两个方面 降低事件与Dom元素的联系 便于保存和恢复组件的状态 降低事件与Dom元素的联系 我们先来看一段传统开发页面交互逻辑时写的代码:

VCL组件开发

4.1 TObject分支 下面几部分分别对包含于五个主要分支中的类做出简要地描述,为完整地说明VCL对象的分层结构,引用了包含于产品中的VCL对象分层结构图. 所有的VCL对象都派生于TObject,其方法定义了构造.析构.消息处理等基本特征.VCL对象的许多强大的功能都建立在TObject给出的这些方法的基础之上.通过提供下列方法,TObject封装了所有VCL对象最基本的通用特征: · 在对象被创建或销毁时响应的能力. · 对象的类型和实例信息,及有关对象发布的属性的运行时类型信息(RTT

LayIM.AspNetCore Middleware 开发日记(一)闲言碎语

前言 前几天写博客的时候突然看见了历史上的今天.不禁感慨时光如梭,这系列博客后来被我标注了已经过时,但是还有很多小伙伴咨询我.既然过时就要更新,正好 .NET Core 也出来很久了,于是乎想到把LayIM融入到.NET Core中做一项服务来用,就像SwaggerUI那样.只需要调用一句 app.UseLayiM() 即可实现LayIM的对接工作.当然,接口越简单,肯定背后承担了复杂的工作.之前我写过基于OWIN和融云的一个Asp.Net的组件,所以稍微有一点经验,但是对于.NET Core的

js组件开发-移动端地区选择控件mobile-select-area

移动端地区选择控件mobile-select-area 由于之前的[js开源组件开发]js手机联动选择地区仿ios 开源git 很受欢迎,于是我又对其进行了一些优化,包括可选的范围变大了,添加了默认空首地址的功能,也添加了更多api参数,首先我们先来看下这次的效果图. 它的github地址请点击https://github.com/tianxiangbing/mobile-select-area 它的demo演示请点击 http://www.lovewebgames.com/jsmodule/m

Vue组件开发分享

在开始本文之前,你可能需要先了解以下相关内容: Vue.js  一款高性能轻量化的MVVM框架 Webpack  前端模块化代码构建工具 Vue组件介绍 基于vue.js高效的双向数据绑定特性,让我们在开发高可用组件时可以更加专注于数据逻辑开发: 忘记DOM操作,忘记事件绑定,让开发的专注力集中于数据上: 1.定义需要使用的数据及类型 2.在合适的时机更新数据 3.在模板上绑定数据与视图的映射关系 4.开放对外调用接口 代码 https://github.com/xiaoyunchen/vue-

CozyRSS开发记录5-订阅列表栏里的项

CozyRSS开发记录5-订阅列表栏里的项 1.订阅列表栏里的项的原型图 这里列表项依然参考傲游的RSS阅读器,以后可能会微调. 2.使用ControlTemplate来定制ListBoxItem 给展示RSS源名称的TextBlock设置MaxWidth和ToolTip,是为了优化名称过长的情况. 这里暂时把内容都写死,后面会使用MVVM和bindling来处理. 3.ListBox效果展示 最后,我们修改ListBox的xaml,用上我们的模版. 效果还行

[转]详解C#组件开发的来龙去脉

C#组件开发首先要了解组件的功能,以及组件为什么会存在.在Visual Studio .NET环境下,将会有新形式的C#组件开发. 组件的功能 微软即将发布的 Visual Studio .NET 将使程序开发人员获得一个集成开发环境,它不但为开发传统的 C/C++ 应用程序,而且也为令人振奋的Microsoft .NET 组件提供了丰富的工具.这些以管理代码编写.在通用语言运行时构建的组件向开发人员提供了一个全新的混合开发环境,即象 Microsoft Visual Basic 一样容易,而同