在Winform混合式框架中整合外部API接口的调用

在我们常规的业务处理中,一般内部处理的接口多数都是以数据库相关的,基于混合式开发的Winform开发框架,虽然在客户端调用的时候,一般选择也是基于Web API的调用,不过后端我们可能不仅仅是针对我们业务数据库的处理,也可以能是调用其他外部接口,如物流、供应商接口等接口,本随笔就是主要介绍基于混合式开发框架如何整合外部API接口的调用。

1、混合式框架的结构介绍

我们知道,混合式的框架是可以在客户端访问Web API服务、WCF服务或者直接连接数据库几种方式的综合,而尤以Web API为最广泛的应用,它的整个框架的结构如下所示。

image
image

在客户端中,通过统一的工厂类CallerFactory<T>对相应的接口进行访问,这里主要就是服务器端Web API服务接口的处理,以及客户端对Web API接口的封装,两部分通过一些基类进行简化处理,可以极大提高开发效率。

对于外部第三方的Web API接口,我们也可以在自己的Web API接口中进行包装,使得客户端通过相应的接口进行交互即可,不需要理会内部还是外部的接口,从而实现透明的接口调用。

2、RFID外部接口的整合处理

在一个客户的应用案例中,需要整合服务商RFID接口实现相应的数据交互,本篇随笔也是基于这个案例进行整个过程的分析和操作,使得我们了解在混合框架中如何整合第三方Web API接口为我们内部框架所用。

一般来说,Web API接口,需要明确API的URL、数据提交方式(POST/GET)、提交参数、返回集合,以及一些特殊的数据等,而一般接口的操作,也是需要一个访问令牌的,这些都是Web API接口调用的关键。

image
image
image

基本上我们有了上面Web API的1/2/3步骤的信息就可以进行接口编程了,这些是Web API开发非常重要的信息。

我们需要特别主要到,步骤1中的信息

image

这里面的token是额外的接口信息,是需要设置Http Request请求的头部信息里面的,是用户身份的重要信息,所以我们一般需要先通过指定的授权接口获取这个token信息。

在这个外部的接口集合里面,我们找到统一登录验证的接口定义如下所示。

image
image

通过上面的分析,我们首先需要需要处理好登录验证接口,然后通过接口传递令牌token给其他接口进行数据处理的。

结合我们的混合框架结构,这里我以测试项目TestProject项目为例进行介绍,我们调整WHC.TestProject.Caller项目的对应类,如下所示。

image

其中Facade层接口类IRFIDService.cs代码如下所示。

    /// <summary>
    /// RFID服务外部接口
    /// </summary>
    [ServiceContract]
    public interface IRFIDService 
    {

        /// <summary>
        /// 终端用户统一登录验证
        /// </summary>
        [OperationContract]
        CheckinResult CheckIn(string username, string password, string device_uuid, string device_type, string last_app_version, string app_id);

        /// <summary>
        /// 获取标签发货通知单批量
        /// </summary>
        [OperationContract]
        TagOrderAsnResult TagOrderAsn(int brand_id, string factcode, string start_time, string end_time, PagerInfo pagerInfo, string token);

        /// <summary>
        /// 标签订单出库物流信息回写
        /// </summary>
        [OperationContract]
        CommonResult TagOutPost(string docno_asn, string factcode, string dest_factcode, List<FreightInfo> freight, string token);
    }

这里面的接口定义,我们是根据输入参数、输出参数进行定义的,另外token是额外增加的令牌参数,用于请求头部写入信息的。

这个接口的定义其实和我们常规的Web API接口定义没有太多的不同,如下是一个内部客户信息接口定义。

    /// <summary>
    /// 客户信息的服务接口
    /// </summary>
    [ServiceContract]
    public interface ICustomerService : IBaseService<CustomerInfo>
    {               
        /// <summary>
        /// 根据客户名称获取客户列表
        /// </summary>
        /// <param name="name">客户名称</param>
        /// <returns></returns>
        [OperationContract]
        List<CustomerInfo> FindByName(string name);
    }

差别就是它们接口继承类有所不同,外部接口由于不需要和数据库打交道,我们不需要继承IBaseService接口

根据这些接口的定义,我们还需要实现我们具体的Web API 服务,逻辑上它是对外部Web API接口的封装,但是对于客户端来说,并不需要知道是内部还是外部接口,客户端只需要知道如果提交参数或者结果即可。

由于Web API涉及多个参数的数据提交,一般来说这种情况都是以POST方式处理的,数据参数则统一在Web API端通过定义一个JObject对象来传递即可,登录认证的Web API接口定义如下所示。

    /// <summary>
    /// 基于RFID的应用接口
    /// </summary>
    public class RFIDController : BaseApiController
    {
        /// <summary>
        /// 终端用户统一登录验证
        /// </summary>
        /// <param name="param">包含多个属性的对象</param>
        /// <param name="token">访问令牌</param>
        [HttpPost]
        public CheckinResult CheckIn(JObject param)
        {
            CheckinResult result = null;
            dynamic obj = param;
            if (obj != null)
            {
                //使用POST数据
                var postData = param.ToJson();
                //使用具体的URL
                var queryUrl = "https://***.***.***/api/v6/rfid/terminal/checkin/post";

                var helper = new HttpHelper();
                helper.ContentType = "application/json";
                var content = helper.GetHtml(queryUrl, postData, true);
                RFIDBaseData<CheckinResult> jsonResult = JsonConvert.DeserializeObject<RFIDBaseData<CheckinResult>>(content);
                if (jsonResult != null && jsonResult.code == 0)
                {
                    result = jsonResult.data;
                }
                return result;
            }
            else
            {
                throw new MyApiException("传递参数错误");
            }
        }

其中输入的参数这里用了JObject param的参数,我们提交给外部Web API接口的时候,我们把这个参数再次序列号为JSON格式的字符串即可:

var postData = param.ToJson();
其中CheckinResult和RFIDBaseData是根据输入参数、输出结果进行的实体类定义,目的是序列化为强类型的实体类,方便数据处理操作。

在客户端,我们只需要对接好和Web API服务端的接口,那么调用起来就非常方便,其中对应的Web API接口客户端封装类 RFIDCaller 如下所示。

    /// <summary>
    /// 对RFID控制的接口调用封装
    /// </summary>
    public class RFIDCaller : NormalApiService, IRFIDService
    {
        public RFIDCaller()
        {
            this.ConfigurationPath = ApiConfig.ConfigFileName; //Web API配置文件
            this.configurationName = ApiConfig.RFID;
        }

        public CheckinResult CheckIn(string username, string password, string device_uuid, string device_type, string last_app_version, string app_id)
        {
            var action = System.Reflection.MethodBase.GetCurrentMethod().Name;
            string url = GetNormalUrl(action);
            var postData = new
            {
                username = username,
                password = password,
                device_uuid = device_uuid,
                device_type = device_type,
                last_app_version = last_app_version,
                app_id = app_id,
            }.ToJson();

            var result = JsonHelper<CheckinResult>.ConvertJson(url, postData);
            return result;
        }

有了这些,我们直接在客户端的界面里面,就可以通过调用CallerFactory<T>进行处理操作了,如下是客户端窗体获取验证身份令牌数据的代码

        private string token = null;//访问RFID接口的token
        /// <summary>
        /// 根据终端用户统一登录验证获取相关访问token
        /// </summary>
        /// <returns></returns>
        private string GetRFIDToken()
        {
            string username = "wuhuacong";
            string password = "123456";
            string device_uuid = "aaaaaaa";
            string device_type = "iphone";
            string last_app_version = "xxxxxxx";
            string app_id = "ntdf5543581a2f066e74cf2fe456";
            
            var result = CallerFactory<IRFIDService>.Instance.CheckIn(username, password, device_uuid, device_type, last_app_version, app_id);
            if(result != null)
            {
                token = result.token;
            }
            return token;
        }

上面是认证身份的接口,其他类型的接口类似的处理方式,如增加了一个

获取标签发货通知单批量
操作后,对应的客户端封装类如下所示。

    /// <summary>
    /// 对RFID控制的接口调用封装
    /// </summary>
    public class RFIDCaller : NormalApiService, IRFIDService
    {
        public RFIDCaller()
        {
            this.ConfigurationPath = ApiConfig.ConfigFileName; //Web API配置文件
            this.configurationName = ApiConfig.RFID;
        }

        public CheckinResult CheckIn(string username, string password, string device_uuid, string device_type, string last_app_version, string app_id)
        {
            var action = System.Reflection.MethodBase.GetCurrentMethod().Name;
            string url = GetNormalUrl(action);
            var postData = new
            {
                username = username,
                password = password,
                device_uuid = device_uuid,
                device_type = device_type,
                last_app_version = last_app_version,
                app_id = app_id,
            }.ToJson();

            var result = JsonHelper<CheckinResult>.ConvertJson(url, postData);
            return result;
        }

        public TagOrderAsnResult TagOrderAsn(int brand_id, string factcode, string start_time, string end_time, Pager.Entity.PagerInfo pagerInfo, string token)
        {
            var action = System.Reflection.MethodBase.GetCurrentMethod().Name;
            string url = GetNormalUrl(action) + string.Format("?token={0}", token);
            var postData = new
            {
                page = pagerInfo.CurrenetPageIndex,
                pagesize = pagerInfo.PageSize,
                brand_id = brand_id,
                factcode = factcode,
                start_time = start_time,
                end_time = end_time,
            }.ToJson();

            var result = JsonHelper<TagOrderAsnResult>.ConvertJson(url, postData);
            return result;
        }

获取标签发货通知单批量
的Web API接口如下代码定义

        /// <summary>
        /// 获取标签发货通知单批量
        /// </summary>
        /// <param name="param"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpPost]
        public TagOrderAsnResult TagOrderAsn(JObject param, string token)
        {
            TagOrderAsnResult result = null;
            dynamic obj = param;
            if (obj != null)
            {
                //使用POST方式
                var postData = param.ToJson();
                var queryUrl = "https://***.***.***/api/v6/rfid/tag/tag_order_asn/get";
                                
                var helper = new HttpHelper();
                helper.ContentType = "application/json";
                helper.Header.Add("token", token); 
                var content = helper.GetHtml(queryUrl, postData, true);

                RFIDBaseData<TagOrderAsnResult> jsonResult = JsonConvert.DeserializeObject<RFIDBaseData<TagOrderAsnResult>>(content);
                if (jsonResult != null && jsonResult.code == 0)
                {
                    result = jsonResult.data;
                }

                return result;
            }
            else
            {
                throw new MyApiException("传递参数错误");
            }

其中表头信息,我们通过下面的代码指定,设置特殊的token表头信息

                var helper = new HttpHelper();
                helper.ContentType = "application/json";
                helper.Header.Add("token", token); 

而在客户端的调用窗体里面,我们调用对应的接口就可以获取该接口的数据了。

        private TagOrderAsnResult asnResult;
        /// <summary>
        /// 根据参数获取标签生产订单批量信息
        /// </summary>
        /// <returns></returns>
        private TagOrderAsnResult GetResult()
        {
            PagerInfo pagerInfo = new PagerInfo() { PageSize = 50, CurrenetPageIndex = 1 };//初始化一个分页条件
            var brand_id = this.txtbrand_id.Text.ToInt32();
            var factcode = this.txtfactcode.Text;
            var start_time = this.txtstart_time.DateTime.ToString("yyyy-MM-dd HH:mm:ss");
            var end_time = this.txtend_time.DateTime.ToString("yyyy-MM-dd HH:mm:ss");

            asnResult = CallerFactory<IRFIDService>.Instance.TagOrderAsn(brand_id, factcode, start_time, end_time, pagerInfo, Token);
            return asnResult;
        }

通过上面的代码演示,我们了解了在混合框架基础上增加外部Web API接口的方法,通过增加Facade层接口,增加Web API接口,以及对应的客户端封装类,具体处理参数根据Web API接口的输入参数、输出数据等信息进行综合处理即可。

最后我们来看看数据的展示界面。

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容