在《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)的更多相关文章

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

    在我这个系列中,我主要以我正在开发的云会员管理系统为例进行介绍Web API的应用,由于云会员的数据设计是支持多个商家公司,而每个公司又可以包含多个店铺的,因此一些字典型的数据需要考虑这方面的不同.如 ...

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

    前面几篇介绍了Web API的基础信息,以及如何基于混合框架的方式在WInform界面里面整合了Web API的接入方式,虽然我们看似调用过程比较复杂,但是基于整个框架的支持和考虑,我们提供了代码生成 ...

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

    最近一直在整合WebAPI.Winform界面.手机短信.微信公众号.企业号等功能,希望把它构建成一个大的应用平台,把我所有的产品线完美连接起来,同时也在探索.攻克更多的技术问题,并抽空写写博客,把相 ...

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

    在上篇随笔<Web API应用架构在Winform混合框架中的应用(1)>中我介绍了关于如何在Winfrom里面整合WebAPI,作为一个新型数据源的接入方式,从而形成了三种不同的数据提供 ...

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

    Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端(包括浏览器,手机和平板电脑等移动设备)的框架, ASP.NET Web API 是一种用于在 .NET Framewor ...

  6. Web API应用架构设计分析(2)

    在上篇随笔<Web API应用架构设计分析(1)>,我对Web API的各种应用架构进行了概括性的分析和设计,Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端 ...

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

    在我们常规的业务处理中,一般内部处理的接口多数都是以数据库相关的,基于混合式开发的Winform开发框架,虽然在客户端调用的时候,一般选择也是基于Web API的调用,不过后端我们可能不仅仅是针对我们 ...

  8. [angularjs] MVC + Web API + AngularJs 搭建简单的 CURD 框架

    MVC + Web API + AngularJs 搭建简单的 CURD 框架 GitHub 地址:https://github.com/liqingwen2015/Wen.MvcSinglePage ...

  9. ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用

    在前面几篇随笔介绍了我对ABP框架的改造,包括对ABP总体的介绍,以及对各个业务分层的简化,Web API 客户端封装层的设计,使得我们基于ABP框架的整体方案越来越清晰化, 也越来越接近实际的项目开 ...

随机推荐

  1. python 对象

    python 对象 在python中,对象就是为C中的结构体在堆上申请的一块内存,一般来说,对象是不能被静态初始化的,并且不能再栈空间上生存.本文主要对Python的基本数据类型做简单的介绍. PyO ...

  2. vi 卡住怎么办

    http://zhidao.baidu.com/question/93283359.html ---------- 我貌似好像按了ctrl+s... 用惯了windows人,会习惯性地按Ctrl+S来 ...

  3. Redis教程(十三):管线详解

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/141.html 一.请求应答协议和RTT: Redis是一种典型的基于C/ ...

  4. SQL中group by的用法

    group by即按照给定字段对结果集进行分组,从字面意义上理解就是根据"by"指定的规则对数据进行分组,所谓的分组就是将一个"数据集"划分成若干个" ...

  5. Atitit vod click event design flow  视频点播系统点击事件文档

    Atitit vod click event design flow  视频点播系统点击事件文档 重构规划1 Click cate1 Click  mov4 重构规划 事件注册,与事件分发管理器分开 ...

  6. Atitit.attilax重要案例 项目与解决方案与成果 v6 qa15

    Atitit.attilax重要案例 项目与解决方案与成果 v6 qa15 1. attilax重要案例的分类(atiuse,auBackTech,bizImp)3 1.1. 两个book3 1.2. ...

  7. 《轻量级Java Web整合开发入门SSH》 - 快速理解Java框架的又一积木

           学习JAVA不难,难的是没有多余的时间给你仔细学习.       伴随着项目的不断跟进,责任重于泰山,必须快速提升.       我不能期望把一本书或者一个项目完全吃透,只希望能用数量去 ...

  8. 3 分钟轻松搭建 Ruby 项目自动化持续集成

    任何事情超过 90 秒就应该自动化,这是程序员的终极打开方式.Automating shapes smarter future. 这是一篇关于 Ruby 项目持续集成的快速指导教程,教大家如何使用 f ...

  9. TypeScript实例

    interface Person { firstName: string, lastName: string } function greeter(person: Person) { return p ...

  10. 第四节:Vue表单标签和组件的基本用法,父子组件间的通信

    vue表单标签和组件的基本用法,父子组件间的通信,直接看例子吧. <!DOCTYPE html> <html> <head> <meta charset=&q ...