• 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来适配。后边还会继续完善。今天就到此为止吧~~

  • 相关阅读:
    sap mm_1
    SAP
    tomcat配置
    sap
    数据库范式
    SAP_20140304
    Eclipse 常用设置
    Oracle常用命令1
    Mysql 学习笔记 20140219
    java 宠物商店代码
  • 原文地址:https://www.cnblogs.com/panzi/p/6958408.html
Copyright © 2020-2023  润新知