Web API应用架构在Winform混合框架中的应用(1)

在《Web API应用架构设计分析(1)》和《Web API应用架构设计分析(2)》中对WebAPI的架构进行了一定的剖析,在当今移动优先的口号下,传统平台都纷纷开发了属于自己的Web API平台,方便各种终端系统的接入,很多企业的需求都是以Web API优先的理念来设计整个企业应用体系的。Web API作为整个纽带的核心,在整个核心层需要考虑到统一性、稳定性、以及安全性等方面因素。本文主要介绍,Web API应用架构,在Winform整合中的角色,以及如何实现在Winform混合架构里面的整合案例。

1、Web API介绍回顾

Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端(包括浏览器,手机和平板电脑等移动设备)的框架, ASP.NET Web API 是一种用于在 .NET Framework 上构建 RESTful 应用程序的理想平台。在目前发达的应用场景下,我们往往需要接入Winform客户端、APP程序、网站程序、以及目前热火朝天的微信应用等,这些数据应该可以由同一个服务提供,这个就是我们所需要构建的Web API平台。由于Web API层作为一个公共的接口层,我们就很好保证了各个界面应用层的数据一致性。

通过上面的了解,我们可以知道,所有外部的应用,其实都可以基于一个相同的Web API核心开展的,如下图所示。

在当前大平台,大应用的背景下,可以基于一个整体的平台,构建很多应用生态链,这样就可以把Web API作为核心层,可以在上面开发我们各种企业业务应用了。

2、Web API在Winform框架中的整合

在Winform界面里面,我们除了可以利用直接访问数据库方式,以及采用访问分布式WCF服务的方式接入,还可以使得它能够访问Web API的数据服务,从而构建成一个适应性更加广泛、功能更加强大的混合式开发框架模式;对于Web API,由于它提供的是一种无状态的接口访问,而且往往Web API一般为了多种客户端接入的需要,可能需要发布在公网上进行访问,因此我们需要更加注重Web API接口层的安全性。

除了直连数据库访问的传统模式,WCF分布式访问的WCF服务访问模式,还可以接入API分布式访问的Web API接口模式,他们的关系构成了一个完整的Winform应用体系,如下图所示。

混合式框架的实现细节,就是通过一个类似开关模式的配置模块,确定是采用直接访问数据库方式,还是访问WCF服务的方式,它们两者是统一到一个Facade接口门面层上,如果考虑到Web API层,基于混合式的架构,也就是在这个Facade接口门面层上增加多一个Web API的接口的封装成即可。具体整个框架的架构图如下所示。

3、Web API访问的安全性考虑

由于Web API是基于互联网的应用,因此安全性要远比在本地访问数据库的要严格的多,基于通用的做法,一般采用几道门槛来处理这些问题,一个是基于CA证书的HTTPS进行数据传输,防止数据被窃听,具体可以参考《Web API应用支持HTTPS的经验总结》;二是采用参数加密签名方式传递,对传递的参数,增加一个加密签名,在服务器端验证签名内容,防止被篡改;三是对一般的接口访问,都需要使用用户身份的token进行校验,只要检查通过才允许访问数据。

Web API接口的访问方式,大概可以分为几类:

1)一个是使用用户令牌,通过Web API接口进行数据访问。这种方式,可以有效识别用户的身份,为用户接口返回用户相关的数据,如包括用户信息维护、密码修改、或者用户联系人等与用户身份相关的数据。

2)一种是使用安全签名进行数据提交。这种方式提交的数据,URL连接的签名参数是经过安全一定规则的加密的,服务器收到数据后也经过同样规则的安全加密,确认数据没有被中途篡改后,再进行数据修改处理。因此我们可以为不同接入方式,如Web/APP/Winfrom等不同接入方式指定不同的加密秘钥,但是秘钥是双方约定的,并不在网络连接上传输,连接传输的一般是这个接入的AppID,服务器通过这个AppID来进行签名参数的加密对比,这种方式,类似微信后台的回调处理机制,它们就是经过这样的处理。

3)一种方式是提供公开的接口调用,不需要传入用户令牌、或者对参数进行加密签名的,这种接口一般较少,只是提供一些很常规的数据显示而已。

基于上面的考虑,我们一般需要设计Web API对象的接口的时候,需要考虑安全性的原因,也就是需要增加更多的一些字段信息了。

如可以在增删改这些接口,除了传入token信息外(标识具体用户),也还是需要传入签名信息,如下接口所示。

        /// <summary>
        /// 插入指定对象到数据库中
        /// </summary>
        /// <param name="info">指定的对象</param>
        /// <returns>执行操作是否成功。</returns>
        public virtual CommonResult Insert(T info, string token, string signature, string timestamp, string nonce, string appid)

上面接口,除了info对象为对象创建的参数外,其他几个参数,都是为了安全性的考虑而加入的。

在接口里面,我们就需要对用户的权限和签名信息进行校验,然后在进行下一步的数据处理,如果校验权限和参数完整性不通过,则会被拦截,不执行数据库的处理了。

            //如果用户token检查不通过,则抛出MyApiException异常。
            //检查用户是否有权限,否则抛出MyDenyAccessException异常
            base.CheckAuthorized(AuthorizeKey.InsertKey, token, signature, timestamp, nonce, appid);

除了这些对数据修改的特殊性接口,有时候我们还需要查找等类似的,不对数据产生变化的接口,只需要传入令牌即可,如下接口所示。

        /// <summary>
        /// 查询数据库,检查是否存在指定ID的对象
        /// </summary>
        /// <param name="id">对象的ID值</param>
        /// <returns>存在则返回指定的对象,否则返回Null</returns>
        [HttpGet]
        public virtual T FindByID(string id, string token)
        {
            //如果用户token检查不通过,则抛出MyApiException异常。
            //检查用户是否有权限,否则抛出MyDenyAccessException异常
            base.CheckAuthorized(AuthorizeKey.ViewKey, token);

            T info = baseBLL.FindByID(id);
            return info;
        }

我们可以看到,上面还是会对token进行校验,不过少了很多签名所需的日期标识、随机数,完整性校验签名,应用ID等参数。

我们会根据用户的token进行解析,如果是正常的token并可以通过解析,那么获取对应用户的权限,判断是否可以进行下一步处理即可。

如果顺利通过,那么访问数据库,把所需的数据返回给调用者即可。

上面提到了用户令牌,用户令牌是一个类似实际生活的通行证,是通过用户名、密码等信息获取到的一个安全令牌,可以在多个接口进行传递的字符串,较少密码参数的传输,提高安全性。

这个用户令牌,一般由单独的接口产生,我们一般放到AuthController里面,这个控制器负责用户令牌相关的处理调用。 

        /// <summary>
        /// 注册用户获取访问令牌接口
        /// </summary>
        /// <param name="username">用户登录名称</param>
        /// <param name="password">用户密码</param>
        /// <param name="signature">加密签名字符串</param>
        /// <param name="timestamp">时间戳</param>
        /// <param name="nonce">随机数</param>
        /// <param name="appid">应用接入ID</param>
        /// <returns></returns>
        TokenResult GetAccessToken(string username, string password,
            string signature, string timestamp, string nonce, string appid);

如下代码是具体业务模块里面,说明如何获取用于操作各种接口的token令牌的,当然,实际环境下,一般都会使用HTTPS协议获取数据了,演示代码如下所示。

                string appid = "myapi_123456";
                string appsecret = "mySecret_2856FB9DBE31";
                //使用API方式,需要在缓存里面设置特殊的信息
                var url = "http://localhost:9001/api/Auth/GetAccessToken" + GetSignatureUrl(appid, appsecret) + "&username=admin&password=";
                TokenResult result = JsonHelper<TokenResult>.ConvertJson(url);
                if(result == null)
                {
                    MessageDxUtil.ShowError("获取授权信息出错,请检查地址是否正确!");
                }

由于Web API的调用,都是一种无状态方式的调用方式,我们通过token来传递我们的用户信息,这样我们只需要验证Token就可以了。JWT的令牌生成逻辑如下所示

令牌生成后,我们需要在Web API调用处理前,对令牌进行校验,确保令牌是正确有效的。

除了令牌的规则,还有一个是加密签名的处理,加密签名需要客户端和服务器端约定相同的秘钥,一般由Web API统一分配,然后传输的时候,客户端使用应用ID即可。

加密签名在服务端(Web API端)的验证流程参考微信的接口的处理方式,处理逻辑如下所示。

1)检查timestamp 与系统时间是否相差在合理时间内,如10分钟。
2)将appSecret、timestamp、nonce三个参数进行字典序排序
3)将三个参数字符串拼接成一个字符串进行SHA1加密
4)加密后的字符串可与signature对比,若匹配则标识该次请求来源于某应用端,请求是合法的。

4、Web API基类设计分析

上面介绍了一些Web API控制器的职能,一般情况下,我们设计一个架构,还需要考虑到基类对象之间的重用关系,尽可能把接口抽象到基类层面上去,减少子类的开发代码量,降低维护成本。

基于上面的目的,参考了我的Web开发框架对于MVC控制器的设计思路

重新整理了Web API的控制器设计对象继承关系,如下所示:

我们关键的核心就是设计好BusinessController<B, T>这个基类,里面设计了大量的通用接口,包括常规的增删改查、分页等处理接口,那么子类继承过来就可以直接拥有这些接口了,多方便啊。

5)Web API客户端(混合式Winform框架模块)的调用

上面介绍了Web API服务端平台的架构设计思路,通过上面的整合,我们减轻了开发重复功能的增删改查等基础功能的控制器代码,把这些接口抽象到接口里面即可实现。

但是我们具体应该如何遵循统一接口层Facade层的约定,然后统一调用WebAPI层的接口,做到悄无声息的从不同的数据源里面获取数据,展示在客户端里面呢。

上面我们分析到,整个混合式Winform框架模块里面,设计方面考虑了数据的获取方面:包含了直接从数据库获取,从WCF服务获取,以及Web API层的数据获取三部分内容,当然还可以有更多的数据接入模式(如WebService等),设计效果如下所示。

所有的数据接入,我们在Facade层都统一到接口里面,客户端的调用也统一到了CallerFactory<T>这个泛型工厂里面,我们根据配置的不同,从不同的模块里面加载,从而实现不同数据源的动态获取了。

如下逻辑就是CallerFactory<T>泛型工厂类的加载逻辑,如下所示:

为了实现简化客户端调用的封装,我们一般也把常规的通用操作封装一下,如下是我原先混合框架里面的设计思路,里面的封装都是通过***Caller的类来进行数据的访问的,这些类统一实现一定关系的集成封装。

为了简化说明调用接口的处理,这里把上面的关系进行了简化,并加入了Web API的调用封装类的处理,几种访问模式下的调用端封装继承关系,如下设计图所示。

最底层的几个DictDataCaller分别是不同访问方式下的接口调用封装类,对于Web API来说,它的访问代码就是如下所示。

        public override bool Delete(string key)
        {
            var action = "Delete";
            string url = GetPostUrlWithToken(action) +string.Format("&id={0}", key);

            CommonResult result = JsonHelper<CommonResult>.ConvertJson(url);
            return result.Success;
        }

        public List<DictDataInfo> FindByTypeID(string dictTypeId)
        {
            var action = "FindByTypeID";
            string url = GetTokenUrl(action) + string.Format("&dictTypeId={0}", dictTypeId);

            List<DictDataInfo> result = JsonHelper<List<DictDataInfo>>.ConvertJson(url);
            return result;
        }

第一个Delete函数是基类提供的,这里进行了重写,一般情况下,不需要处理就具备增删改分页等基础接口的调用封装了。

由于所有的实现类都实现继承了统一的Facade层的接口,那么统一调用也就是自然而然的事情了。所以在Winform界面里面,所有的调用都是使用CallerFactory<T>进行了统一的处理,数据访问的不同不影响接口的处理, 三种方式的数据调用,统一都是下面的代码进行处理。

            DictDataInfo info = CallerFactory<IDictDataService>.Instance.FindByID(ID);

            if (info != null)
            {
                SetInfo(info);

                try
                {
                    bool succeed = CallerFactory<IDictDataService>.Instance.Update(info, info.ID.ToString());
                    return succeed;
                }
                catch (Exception ex)
                {
                    LogTextHelper.Error(ex);
                    MessageDxUtil.ShowError(ex.Message);
                }
            }

系列文章如下所示:

Web API应用架构在Winform混合框架中的应用(1)

Web API应用架构在Winform混合框架中的应用(2)--自定义异常结果的处理

Web API接口设计经验总结 

Web API应用架构在Winform混合框架中的应用(3)--Winfrom界面调用WebAPI的过程分解

Web API应用架构在Winform混合框架中的应用(4)--利用代码生成工具快速开发整套应用

Web API应用架构在Winform混合框架中的应用(5)--系统级别字典和公司级别字典并存的处理方式

本文转自博客园伍华聪的博客,原文链接:Web API应用架构在Winform混合框架中的应用(1),如需转载请自行联系原博主。

时间: 2024-08-02 05:02:31

Web API应用架构在Winform混合框架中的应用(1)的相关文章

Web API应用架构在Winform混合框架中的应用(2)--自定义异常结果的处理

在上篇随笔<Web API应用架构在Winform混合框架中的应用(1)>中我介绍了关于如何在Winfrom里面整合WebAPI,作为一个新型数据源的接入方式,从而形成了三种不同的数据提供来源,前文在介绍整体性框架方面,着笔较多,注重整合的统一性,但是在Web API方面还不算很具体,本系列将继续这个主题,介绍Web API开发中常见到的一些问题,对其中各个技术要点进行总结,本文主要介绍Web API自定义异常结果的处理. 1.常规的异常处理 统一的异常处理,把正确的信息返回给调用者很重要,可

Web API应用架构在Winform混合框架中的应用(5)--系统级别字典和公司级别字典并存的处理方式

在我这个系列中,我主要以我正在开发的云会员管理系统为例进行介绍Web API的应用,由于云会员的数据设计是支持多个商家公司,而每个公司又可以包含多个店铺的,因此一些字典型的数据需要考虑这方面的不同.如对于证件类型,收费处理状态,民族,职称等这些固定化的内容,我们可以放到全局字典里面,但是对于一些如会员相关的字典数据,如产品单位.产品类型等内容,如果也全部规定为全局的系统字典,那么就缺乏灵活性,这些数据应该可以由各自进行差异化处理. 1.云会员系统的字典数据模型 我们先来了解下基于Web API接

Web API应用架构概括分析和设计实例教程

1.Web API的核心层设计 在目前发达的应用场景下,我们往往需要接入Winform客户端.APP程序.网站程序.以及目前热火朝天的微信应用等,这些数据应该可以由同一个服务提供,这个就是我们所需要构建的Web API平台,基于上述的需求,很多企业的需求都是以Web API优先的理念来设计整个企业应用体系的.Web API作为整个纽带的核心,在整个核心层需要考虑到统一性.稳定性.以及安全性等方面因素. 从上图我们可以看到,整个外围的应用场景围绕着Web API核心层构建,如果我们把它换一种方式表

Web API应用架构设计分析(1)

Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端(包括浏览器,手机和平板电脑等移动设备)的框架, ASP.NET Web API 是一种用于在 .NET Framework 上构建 RESTful 应用程序的理想平台.本文主要以ASP.NET Web API 的框架实现来介绍整个Web API应用架构设计,但不局限于.NET的技术. 1.Web API的核心层设计 在目前发达的应用场景下,我们往往需要接入Winform客户端.APP程序.网站程序.以及目前热火朝天的

混合框架中Oracle数据库的还原处理操作

在较早期的随笔<Oracle如何实现创建数据库.备份数据库及数据导出导入的一条龙操作>粗略介绍了Oracle数据库的备份还原操作,本文想从开发框架的基础上介绍Oracle数据库的脚本或者还原操作. 我们在Winform开发框架.混合式开发框架.Web开发框架等各种开发框架,底层都是支持多种数据库的,如MS SQLServer.Oracle.MySQL.SQLite.PostgreSQL等等,如下图所示. 其中SQLServer支持最为常用,而且也是最为方便备份还原的操作,而Oracle数据库这

可否把arcgis api for javascript整合到primefaces框架中?

问题描述 primefaces框架是基于jsf的,很漂亮!!!如果可以,可否给个实例代码?如果不可以,那为什么呢? 解决方案 解决方案二: 自己搞定了.

Winform混合式开发框架访问Web API接口的处理

在我的混合式开发框架里面,集成了WebAPI的访问,这种访问方式不仅可以实现简便的数据交换,而且可以在多种平台上进行接入,如Winform程序.Web网站.移动端APP等多种接入方式,Web API的处理方式和微信提供的接口处理规则类似,也是通过向服务器获得访问令牌(AccessToken),然后传递给每个Web API接口,实现数据的交换处理.本篇随笔主要介绍混合框架中Winform对Web API访问的处理. 1.Web API接入方式介绍 <混合式开发框架>混合了Web API接口访问.

Web API项目中使用Area对业务进行分类管理

在之前开发的很多Web API项目中,为了方便以及快速开发,往往把整个Web API的控制器放在基目录的Controllers目录中,但随着业务越来越复杂,这样Controllers目录中的文件就增加很快,难以管理,而且如果有不同业务模块有重复的控制器名的话,还需要尽量避免.引入Area的作用就是把控制器按照不同的业务模块进行区分,方便管理,而且控制器名称可以重名. 1.Web API项目引入Area进行分类 Area在项目中可以称之为区域,每个Area代表应用程序的不同功能模块,Area 使每

Web API接口设计经验总结

在Web API接口的开发过程中,我们可能会碰到各种各样的问题,我在前面两篇随笔<Web API应用架构在Winform混合框架中的应用(1)>.<Web API应用架构在Winform混合框架中的应用(2)--自定义异常结果的处理>也进行了总的介绍,在经过我的大量模块实践并成功运行后,总结了这篇随笔,希望对大家有所帮助. 1.在接口定义中确定MVC的GET或者POST方式 由于我们整个Web API平台是基于MVC的基础上进行的API开发,因此整个Web API的接口,在定义的时