- 浏览: 106260 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
u013246812:
谢谢博主帮我解决了问题,就是那个process.exitVal ...
Java执行Shell脚本超时控制 -
fireinjava:
fireinjava 写道配置好多哦 =.=
刚看了下,原来是 ...
Java Spring2.5 Remote Invoke HTTP Invoker -
fireinjava:
配置好多哦 =.=
Java Spring2.5 Remote Invoke HTTP Invoker -
lee79:
呵呵,讲的很对
Java执行Shell脚本超时控制 -
fangwei:
非常感谢!!!btw 你虽然用到了slf4j,却没有用到它的强 ...
Java执行Shell脚本超时控制
Web API应用架构在Winform混合框架中的应用(1)
<div class="iteye-blog-content-contain" style="font-size: 14px;"><div id="cnblogs_post_body">
<p>在《<a id="cb_post_title_url" href="http://www.cnblogs.com/wuhuacong/p/4614875.html">Web API应用架构设计分析(1)</a>》和《<a id="cb_post_title_url" href="http://www.cnblogs.com/wuhuacong/p/4620300.html">Web API应用架构设计分析(2)</a>》 中对WebAPI的架构进行了一定的剖析,在当今移动优先的口号下,传统平台都纷纷开发了属于自己的Web API平台,方便各种终端系统的接入,很多企业的需求都是以Web API优先的理念来设计整个企业应用体系的。Web API作为整个纽带的核心,在整个核心层需要考虑到统一性、稳定性、以及安全性等方面因素。本文主要介绍,Web API应用架构,在Winform整合中的角色,以及如何实现在Winform混合架构里面的整合案例。</p>
<h3>1、Web API介绍回顾</h3>
<p>Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端(包括浏览器,手机和平板电脑等移动设备)的框架, <span id="mt2" class="sentence">ASP.NET Web API 是一种用于在 .NET Framework 上构建 RESTful 应用程序的理想平台。在目前发达的应用场景下,我们往往需要接入Winform客户端、APP程序、网站程序、以及目前热火朝天的微信应用等,这些数据应 该可以由同一个服务提供,这个就是我们所需要构建的Web API平台。由于Web API层作为一个公共的接口层,我们就很好保证了各个界面应用层的数据一致性。</span></p>
<p>通过上面的了解,我们可以知道,所有外部的应用,其实都可以基于一个相同的Web API核心开展的,如下图所示。</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/012319146255372.png" alt="" width="402" height="455"></p>
<p>在当前大平台,大应用的背景下,可以基于一个整体的平台,构建很多应用生态链,这样就可以把Web API作为核心层,可以在上面开发我们各种企业业务应用了。</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/012337464536202.png" alt="" width="586" height="576"></p>
<p> </p>
<h3>2、Web API在Winform框架中的整合</h3>
<p>在Winform界面里面,我们除了可以利用直接访问数据库方式,以及采用访问分布式WCF服务的方式接入,还可以使得它能够访问Web API的数据服务,从而构建成一个适应性更加广泛、功能更加强大的混合式开发框架模式;对于Web API,由于它提供的是一种无状态的接口访问,而且往往Web API一般为了多种客户端接入的需要,可能需要发布在公网上进行访问,因此我们需要更加注重Web API接口层的安全性。</p>
<p>除了直连数据库访问的传统模式,WCF分布式访问的WCF服务访问模式,还可以接入API分布式访问的Web API接口模式,他们的关系构成了一个完整的Winform应用体系,如下图所示。</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/012339136878450.png" alt="" width="591" height="444"></p>
<p>混合式框架的实现细节,就是通过一个类似开关模式的配置模块,确定是采用直接访问数据库方式,还是访问WCF服务的方式,它们两者是统一到一个 Facade接口门面层上,如果考虑到Web API层,基于混合式的架构,也就是在这个Facade接口门面层上增加多一个Web API的接口的封装成即可。具体整个框架的架构图如下所示。</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/012340271713269.png" alt=""></p>
<p> </p>
<h3>3、Web API访问的安全性考虑</h3>
<p>由于Web API是基于互联网的应用,因此安全性要远比在本地访问数据库的要严格的多,基于通用的做法,一般采用几道门槛来处理这些问题,一个是基于CA证书的HTTPS进行数据传输,防止数据被,具体可以参考《<a id="cb_post_title_url" href="http://www.cnblogs.com/wuhuacong/p/4814708.html">Web API应用支持HTTPS的经验总结</a>》;二是采用参数加密签名方式传递,对传递的参数,增加一个加密签名,在服务器端验证签名内容,防止被篡改;三是对一般的接口访问,都需要使用用户身份的token进行校验,只要检查通过才允许访问数据。</p>
<p>Web API接口的访问方式,大概可以分为几类:</p>
<p>1)一个是使用用户令牌,通过Web API接口进行数据访问。这种方式,可以有效识别用户的身份,为用户接口返回用户相关的数据,如包括用户信息维护、密码修改、或者用户联系人等与用户身份相关的数据。</p>
<p>2)一种是使用安全签名进行数据提交。这种方式提交的数据,URL连接的签名参数是经过安全一定规则的加密的,服务器收到数据后也经过同样规则的安 全加密,确认数据没有被中途篡改后,再进行数据修改处理。因此我们可以为不同接入方式,如Web/APP/Winfrom等不同接入方式指定不同的加密秘 钥,但是秘钥是双方约定的,并不在网络连接上传输,连接传输的一般是这个接入的AppID,服务器通过这个AppID来进行签名参数的加密对比,这种方 式,类似微信后台的回调处理机制,它们就是经过这样的处理。</p>
<p>3)一种方式是提供公开的接口调用,不需要传入用户令牌、或者对参数进行加密签名的,这种接口一般较少,只是提供一些很常规的数据显示而已。</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925145439069-108490328.png" alt=""></p>
<p>基于上面的考虑,我们一般需要设计Web API对象的接口的时候,需要考虑安全性的原因,也就是需要增加更多的一些字段信息了。</p>
<p>如可以在增删改这些接口,除了传入token信息外(标识具体用户),也还是需要传入签名信息,如下接口所示。</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span>
<span style="color: #808080;">///</span><span style="color: #008000;"> 插入指定对象到数据库中
</span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="info"></span><span style="color: #008000;">指定的对象</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><returns></span><span style="color: #008000;">执行操作是否成功。</span><span style="color: #808080;"></returns></span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">virtual</span> CommonResult Insert(T info, <span style="color: #0000ff;">string</span> token, <span style="color: #0000ff;">string</span> signature, <span style="color: #0000ff;">string</span> timestamp, <span style="color: #0000ff;">string</span> nonce, <span style="color: #0000ff;">string</span> appid)</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p>上面接口,除了info对象为对象创建的参数外,其他几个参数,都是为了安全性的考虑而加入的。</p>
<p>在接口里面,我们就需要对用户的权限和签名信息进行校验,然后在进行下一步的数据处理,如果校验权限和参数完整性不通过,则会被拦截,不执行数据库的处理了。</p>
<div class="cnblogs_code">
<pre> <span style="color: #008000;">//</span><span style="color: #008000;">如果用户token检查不通过,则抛出MyApiException异常。
</span><span style="color: #008000;">//</span><span style="color: #008000;">检查用户是否有权限,否则抛出MyDenyAccessException异常</span>
<span style="color: #0000ff;">base</span>.CheckAuthorized(AuthorizeKey.InsertKey, token, signature, timestamp, nonce, appid);</pre>
</div>
<p><span style="line-height: 1.5;">除了这些对数据修改的特殊性接口,有时候我们还需要查找等类似的,不对数据产生变化的接口,只需要传入令牌即可,如下接口所示。</span></p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span>
<span style="color: #808080;">///</span><span style="color: #008000;"> 查询数据库,检查是否存在指定ID的对象
</span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="id"></span><span style="color: #008000;">对象的ID值</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><returns></span><span style="color: #008000;">存在则返回指定的对象,否则返回Null</span><span style="color: #808080;"></returns></span>
<span style="color: #000000;"> [HttpGet]
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">virtual</span> T FindByID(<span style="color: #0000ff;">string</span> id, <span style="color: #0000ff;">string</span><span style="color: #000000;"> token)
{
</span><span style="color: #008000;">//</span><span style="color: #008000;">如果用户token检查不通过,则抛出MyApiException异常。
</span><span style="color: #008000;">//</span><span style="color: #008000;">检查用户是否有权限,否则抛出MyDenyAccessException异常</span>
<span style="color: #0000ff;">base</span><span style="color: #000000;">.CheckAuthorized(AuthorizeKey.ViewKey, token);
T info </span>=<span style="color: #000000;"> baseBLL.FindByID(id);
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> info;
}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p>我们可以看到,上面还是会对token进行校验,不过少了很多签名所需的日期标识、随机数,完整性校验签名,应用ID等参数。</p>
<p>我们会根据用户的token进行解析,如果是正常的token并可以通过解析,那么获取对应用户的权限,判断是否可以进行下一步处理即可。</p>
<p>如果顺利通过,那么访问数据库,把所需的数据返回给调用者即可。</p>
<p>上面提到了用户令牌,用户令牌是一个类似实际生活的通行证,是通过用户名、密码等信息获取到的一个安全令牌,可以在多个接口进行传递的字符串,较少密码参数的传输,提高安全性。</p>
<p>这个用户令牌,一般由单独的接口产生,我们一般放到AuthController里面,这个控制器负责用户令牌相关的处理调用。 </p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span>
<span style="color: #808080;">///</span><span style="color: #008000;"> 注册用户获取访问令牌接口
</span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="username"></span><span style="color: #008000;">用户登录名称</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="password"></span><span style="color: #008000;">用户密码</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="signature"></span><span style="color: #008000;">加密签名字符串</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="timestamp"></span><span style="color: #008000;">时间戳</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="nonce"></span><span style="color: #008000;">随机数</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="appid"></span><span style="color: #008000;">应用接入ID</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><returns></returns></span>
TokenResult GetAccessToken(<span style="color: #0000ff;">string</span> username, <span style="color: #0000ff;">string</span><span style="color: #000000;"> password,
</span><span style="color: #0000ff;">string</span> signature, <span style="color: #0000ff;">string</span> timestamp, <span style="color: #0000ff;">string</span> nonce, <span style="color: #0000ff;">string</span> appid);</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p>如下代码是具体业务模块里面,说明如何获取用于操作各种接口的token令牌的,当然,实际环境下,一般都会使用HTTPS协议获取数据了,演示代码如下所示。</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #0000ff;">string</span> appid = <span style="color: #800000;">"</span><span style="color: #800000;">myapi_123456</span><span style="color: #800000;">"</span><span style="color: #000000;">;
</span><span style="color: #0000ff;">string</span> appsecret = <span style="color: #800000;">"</span><span style="color: #800000;">mySecret_2856FB9DBE31</span><span style="color: #800000;">"</span><span style="color: #000000;">;
</span><span style="color: #008000;">//</span><span style="color: #008000;">使用API方式,需要在缓存里面设置特殊的信息</span>
<span style="color: #0000ff;">var</span> url = <span style="color: #800000;">"</span><span style="color: #800000;">http://localhost:9001/api/Auth/GetAccessToken</span><span style="color: #800000;">"</span> + GetSignatureUrl(appid, appsecret) + <span style="color: #800000;">"</span><span style="color: #800000;">&username=admin&password=</span><span style="color: #800000;">"</span><span style="color: #000000;">;
TokenResult result </span>= JsonHelper<TokenResult><span style="color: #000000;">.ConvertJson(url);
</span><span style="color: #0000ff;">if</span>(result == <span style="color: #0000ff;">null</span><span style="color: #000000;">)
{
MessageDxUtil.ShowError(</span><span style="color: #800000;">"</span><span style="color: #800000;">获取授权信息出错,请检查地址是否正确!</span><span style="color: #800000;">"</span><span style="color: #000000;">);
}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p> </p>
<p>由于Web API的调用,都是一种无状态方式的调用方式,我们通过token来传递我们的用户信息,这样我们只需要验证Token就可以了。JWT的令牌生成逻辑如下所示</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/041143099993045.png" alt=""></p>
<p>令牌生成后,我们需要在Web API调用处理前,对令牌进行校验,确保令牌是正确有效的。</p>
<p> </p>
<p>除了令牌的规则,还有一个是加密签名的处理,加密签名需要客户端和服务器端约定相同的秘钥,一般由Web API统一分配,然后传输的时候,客户端使用应用ID即可。</p>
<p>加密签名在服务端(Web API端)的验证流程参考微信的接口的处理方式,处理逻辑如下所示。</p>
<p>1)检查timestamp 与系统时间是否相差在合理时间内,如10分钟。<br>2)将appSecret、timestamp、nonce三个参数进行字典序排序<br>3)将三个参数字符串拼接成一个字符串进行SHA1加密<br>4)加密后的字符串可与signature对比,若匹配则标识该次请求来源于某应用端,请求是合法的。</p>
<p> </p>
<h3>4、Web API基类设计分析</h3>
<p>上面介绍了一些Web API控制器的职能,一般情况下,我们设计一个架构,还需要考虑到基类对象之间的重用关系,尽可能把接口抽象到基类层面上去,减少子类的开发代码量,降低维护成本。</p>
<p>基于上面的目的,参考了我的Web开发框架对于MVC控制器的设计思路</p>
<p><img style="width: 867px;" src="http://images.cnitblog.com/blog/8867/201308/27120607-6ad84bc6809c4e0fa5d46150569ce385.png" alt=""></p>
<p>重新整理了Web API的控制器设计对象继承关系,如下所示:</p>
<p><img style="width: 867px;" src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925152731178-729280110.png" alt=""></p>
<p>我们关键的核心就是设计好<span style="color: #ff0000;"><strong>BusinessController<B, T></strong></span>这个基类,里面设计了大量的通用接口,包括常规的增删改查、分页等处理接口,那么子类继承过来就可以直接拥有这些接口了,多方便啊。</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925155916662-113368530.png" alt=""></p>
<p><img style="width: 867px;" src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925160136569-599589316.png" alt=""></p>
<p> </p>
<h3>5)Web API客户端(混合式Winform框架模块)的调用</h3>
<p>上面介绍了Web API服务端平台的架构设计思路,通过上面的整合,我们减轻了开发重复功能的增删改查等基础功能的控制器代码,把这些接口抽象到接口里面即可实现。</p>
<p>但是我们具体应该如何遵循统一接口层Facade层的约定,然后统一调用WebAPI层的接口,做到悄无声息的从不同的数据源里面获取数据,展示在客户端里面呢。</p>
<p>上面我们分析到,整个混合式Winform框架模块里面,设计方面考虑了数据的获取方面:包含了直接从数据库获取,从WCF服务获取,以及Web API层的数据获取三部分内容,当然还可以有更多的数据接入模式(如WebService等),设计效果如下所示。</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925160534975-451803597.png" alt=""></p>
<p>所有的数据接入,我们在Facade层都统一到接口里面,客户端的调用也统一到了CallerFactory<T>这个泛型工厂里面,我们根据配置的不同,从不同的模块里面加载,从而实现不同数据源的动态获取了。</p>
<p>如下逻辑就是CallerFactory<T>泛型工厂类的加载逻辑,如下所示:</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925161033428-1524737338.png" alt=""></p>
<p>为了实现简化客户端调用的封装,我们一般也把常规的通用操作封装一下,如下是我原先混合框架里面的设计思路,里面的封装都是通过***Caller的类来进行数据的访问的,这些类统一实现一定关系的集成封装。</p>
<p><img src="http://images.cnitblog.com/blog/8867/201304/11201859-da8bc43ac3a940fab9e3f984543fe11d.png" alt=""></p>
<p>为了简化说明调用接口的处理,这里把上面的关系进行了简化,并加入了Web API的调用封装类的处理,几种访问模式下的调用端封装继承关系,如下设计图所示。</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925162813725-1174476473.png" alt=""></p>
<p>最底层的几个DictDataCaller分别是不同访问方式下的接口调用封装类,对于Web API来说,它的访问代码就是如下所示。</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">bool</span> Delete(<span style="color: #0000ff;">string</span><span style="color: #000000;"> key)
{
</span><span style="color: #0000ff;">var</span> action = <span style="color: #800000;">"</span><span style="color: #800000;">Delete</span><span style="color: #800000;">"</span><span style="color: #000000;">;
</span><span style="color: #0000ff;">string</span> url = GetPostUrlWithToken(action) +<span style="color: #0000ff;">string</span>.Format(<span style="color: #800000;">"</span><span style="color: #800000;">&id={0}</span><span style="color: #800000;">"</span><span style="color: #000000;">, key);
CommonResult result </span>= JsonHelper<CommonResult><span style="color: #000000;">.ConvertJson(url);
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result.Success;
}
</span><span style="color: #0000ff;">public</span> List<DictDataInfo> FindByTypeID(<span style="color: #0000ff;">string</span><span style="color: #000000;"> dictTypeId)
{
</span><span style="color: #0000ff;">var</span> action = <span style="color: #800000;">"</span><span style="color: #800000;">FindByTypeID</span><span style="color: #800000;">"</span><span style="color: #000000;">;
</span><span style="color: #0000ff;">string</span> url = GetTokenUrl(action) + <span style="color: #0000ff;">string</span>.Format(<span style="color: #800000;">"</span><span style="color: #800000;">&dictTypeId={0}</span><span style="color: #800000;">"</span><span style="color: #000000;">, dictTypeId);
List</span><DictDataInfo> result = JsonHelper<List<DictDataInfo>><span style="color: #000000;">.ConvertJson(url);
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result;
}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p>第一个Delete函数是基类提供的,这里进行了重写,一般情况下,不需要处理就具备增删改分页等基础接口的调用封装了。</p>
<p>由于所有的实现类都实现继承了统一的Facade层的接口,那么统一调用也就是自然而然的事情了。所以在Winform界面里面,所有的调用都是使 用CallerFactory<T>进行了统一的处理,数据访问的不同不影响接口的处理, 三种方式的数据调用,统一都是下面的代码进行处理。</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #ff0000;"> DictDataInfo info = <strong>CallerFactory<IDictDataService>.Instance</strong>.FindByID(ID);
</span><span style="color: #0000ff;">if</span> (info != <span style="color: #0000ff;">null</span><span style="color: #000000;">)
{
SetInfo(info);
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">
{
</span><span style="color: #ff0000;">bool succeed = <strong>CallerFactory<IDictDataService>.Instance</strong>.Update(info, info.ID.ToString());
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> succeed;
}
</span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p> http://www.cnblogs.com/wuhuacong/p/4838220.html</p>
</div></div>
<p>在《<a id="cb_post_title_url" href="http://www.cnblogs.com/wuhuacong/p/4614875.html">Web API应用架构设计分析(1)</a>》和《<a id="cb_post_title_url" href="http://www.cnblogs.com/wuhuacong/p/4620300.html">Web API应用架构设计分析(2)</a>》 中对WebAPI的架构进行了一定的剖析,在当今移动优先的口号下,传统平台都纷纷开发了属于自己的Web API平台,方便各种终端系统的接入,很多企业的需求都是以Web API优先的理念来设计整个企业应用体系的。Web API作为整个纽带的核心,在整个核心层需要考虑到统一性、稳定性、以及安全性等方面因素。本文主要介绍,Web API应用架构,在Winform整合中的角色,以及如何实现在Winform混合架构里面的整合案例。</p>
<h3>1、Web API介绍回顾</h3>
<p>Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端(包括浏览器,手机和平板电脑等移动设备)的框架, <span id="mt2" class="sentence">ASP.NET Web API 是一种用于在 .NET Framework 上构建 RESTful 应用程序的理想平台。在目前发达的应用场景下,我们往往需要接入Winform客户端、APP程序、网站程序、以及目前热火朝天的微信应用等,这些数据应 该可以由同一个服务提供,这个就是我们所需要构建的Web API平台。由于Web API层作为一个公共的接口层,我们就很好保证了各个界面应用层的数据一致性。</span></p>
<p>通过上面的了解,我们可以知道,所有外部的应用,其实都可以基于一个相同的Web API核心开展的,如下图所示。</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/012319146255372.png" alt="" width="402" height="455"></p>
<p>在当前大平台,大应用的背景下,可以基于一个整体的平台,构建很多应用生态链,这样就可以把Web API作为核心层,可以在上面开发我们各种企业业务应用了。</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/012337464536202.png" alt="" width="586" height="576"></p>
<p> </p>
<h3>2、Web API在Winform框架中的整合</h3>
<p>在Winform界面里面,我们除了可以利用直接访问数据库方式,以及采用访问分布式WCF服务的方式接入,还可以使得它能够访问Web API的数据服务,从而构建成一个适应性更加广泛、功能更加强大的混合式开发框架模式;对于Web API,由于它提供的是一种无状态的接口访问,而且往往Web API一般为了多种客户端接入的需要,可能需要发布在公网上进行访问,因此我们需要更加注重Web API接口层的安全性。</p>
<p>除了直连数据库访问的传统模式,WCF分布式访问的WCF服务访问模式,还可以接入API分布式访问的Web API接口模式,他们的关系构成了一个完整的Winform应用体系,如下图所示。</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/012339136878450.png" alt="" width="591" height="444"></p>
<p>混合式框架的实现细节,就是通过一个类似开关模式的配置模块,确定是采用直接访问数据库方式,还是访问WCF服务的方式,它们两者是统一到一个 Facade接口门面层上,如果考虑到Web API层,基于混合式的架构,也就是在这个Facade接口门面层上增加多一个Web API的接口的封装成即可。具体整个框架的架构图如下所示。</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/012340271713269.png" alt=""></p>
<p> </p>
<h3>3、Web API访问的安全性考虑</h3>
<p>由于Web API是基于互联网的应用,因此安全性要远比在本地访问数据库的要严格的多,基于通用的做法,一般采用几道门槛来处理这些问题,一个是基于CA证书的HTTPS进行数据传输,防止数据被,具体可以参考《<a id="cb_post_title_url" href="http://www.cnblogs.com/wuhuacong/p/4814708.html">Web API应用支持HTTPS的经验总结</a>》;二是采用参数加密签名方式传递,对传递的参数,增加一个加密签名,在服务器端验证签名内容,防止被篡改;三是对一般的接口访问,都需要使用用户身份的token进行校验,只要检查通过才允许访问数据。</p>
<p>Web API接口的访问方式,大概可以分为几类:</p>
<p>1)一个是使用用户令牌,通过Web API接口进行数据访问。这种方式,可以有效识别用户的身份,为用户接口返回用户相关的数据,如包括用户信息维护、密码修改、或者用户联系人等与用户身份相关的数据。</p>
<p>2)一种是使用安全签名进行数据提交。这种方式提交的数据,URL连接的签名参数是经过安全一定规则的加密的,服务器收到数据后也经过同样规则的安 全加密,确认数据没有被中途篡改后,再进行数据修改处理。因此我们可以为不同接入方式,如Web/APP/Winfrom等不同接入方式指定不同的加密秘 钥,但是秘钥是双方约定的,并不在网络连接上传输,连接传输的一般是这个接入的AppID,服务器通过这个AppID来进行签名参数的加密对比,这种方 式,类似微信后台的回调处理机制,它们就是经过这样的处理。</p>
<p>3)一种方式是提供公开的接口调用,不需要传入用户令牌、或者对参数进行加密签名的,这种接口一般较少,只是提供一些很常规的数据显示而已。</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925145439069-108490328.png" alt=""></p>
<p>基于上面的考虑,我们一般需要设计Web API对象的接口的时候,需要考虑安全性的原因,也就是需要增加更多的一些字段信息了。</p>
<p>如可以在增删改这些接口,除了传入token信息外(标识具体用户),也还是需要传入签名信息,如下接口所示。</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span>
<span style="color: #808080;">///</span><span style="color: #008000;"> 插入指定对象到数据库中
</span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="info"></span><span style="color: #008000;">指定的对象</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><returns></span><span style="color: #008000;">执行操作是否成功。</span><span style="color: #808080;"></returns></span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">virtual</span> CommonResult Insert(T info, <span style="color: #0000ff;">string</span> token, <span style="color: #0000ff;">string</span> signature, <span style="color: #0000ff;">string</span> timestamp, <span style="color: #0000ff;">string</span> nonce, <span style="color: #0000ff;">string</span> appid)</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p>上面接口,除了info对象为对象创建的参数外,其他几个参数,都是为了安全性的考虑而加入的。</p>
<p>在接口里面,我们就需要对用户的权限和签名信息进行校验,然后在进行下一步的数据处理,如果校验权限和参数完整性不通过,则会被拦截,不执行数据库的处理了。</p>
<div class="cnblogs_code">
<pre> <span style="color: #008000;">//</span><span style="color: #008000;">如果用户token检查不通过,则抛出MyApiException异常。
</span><span style="color: #008000;">//</span><span style="color: #008000;">检查用户是否有权限,否则抛出MyDenyAccessException异常</span>
<span style="color: #0000ff;">base</span>.CheckAuthorized(AuthorizeKey.InsertKey, token, signature, timestamp, nonce, appid);</pre>
</div>
<p><span style="line-height: 1.5;">除了这些对数据修改的特殊性接口,有时候我们还需要查找等类似的,不对数据产生变化的接口,只需要传入令牌即可,如下接口所示。</span></p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span>
<span style="color: #808080;">///</span><span style="color: #008000;"> 查询数据库,检查是否存在指定ID的对象
</span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="id"></span><span style="color: #008000;">对象的ID值</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><returns></span><span style="color: #008000;">存在则返回指定的对象,否则返回Null</span><span style="color: #808080;"></returns></span>
<span style="color: #000000;"> [HttpGet]
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">virtual</span> T FindByID(<span style="color: #0000ff;">string</span> id, <span style="color: #0000ff;">string</span><span style="color: #000000;"> token)
{
</span><span style="color: #008000;">//</span><span style="color: #008000;">如果用户token检查不通过,则抛出MyApiException异常。
</span><span style="color: #008000;">//</span><span style="color: #008000;">检查用户是否有权限,否则抛出MyDenyAccessException异常</span>
<span style="color: #0000ff;">base</span><span style="color: #000000;">.CheckAuthorized(AuthorizeKey.ViewKey, token);
T info </span>=<span style="color: #000000;"> baseBLL.FindByID(id);
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> info;
}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p>我们可以看到,上面还是会对token进行校验,不过少了很多签名所需的日期标识、随机数,完整性校验签名,应用ID等参数。</p>
<p>我们会根据用户的token进行解析,如果是正常的token并可以通过解析,那么获取对应用户的权限,判断是否可以进行下一步处理即可。</p>
<p>如果顺利通过,那么访问数据库,把所需的数据返回给调用者即可。</p>
<p>上面提到了用户令牌,用户令牌是一个类似实际生活的通行证,是通过用户名、密码等信息获取到的一个安全令牌,可以在多个接口进行传递的字符串,较少密码参数的传输,提高安全性。</p>
<p>这个用户令牌,一般由单独的接口产生,我们一般放到AuthController里面,这个控制器负责用户令牌相关的处理调用。 </p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></span>
<span style="color: #808080;">///</span><span style="color: #008000;"> 注册用户获取访问令牌接口
</span><span style="color: #808080;">///</span> <span style="color: #808080;"></summary></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="username"></span><span style="color: #008000;">用户登录名称</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="password"></span><span style="color: #008000;">用户密码</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="signature"></span><span style="color: #008000;">加密签名字符串</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="timestamp"></span><span style="color: #008000;">时间戳</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="nonce"></span><span style="color: #008000;">随机数</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><param name="appid"></span><span style="color: #008000;">应用接入ID</span><span style="color: #808080;"></param></span>
<span style="color: #808080;">///</span> <span style="color: #808080;"><returns></returns></span>
TokenResult GetAccessToken(<span style="color: #0000ff;">string</span> username, <span style="color: #0000ff;">string</span><span style="color: #000000;"> password,
</span><span style="color: #0000ff;">string</span> signature, <span style="color: #0000ff;">string</span> timestamp, <span style="color: #0000ff;">string</span> nonce, <span style="color: #0000ff;">string</span> appid);</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p>如下代码是具体业务模块里面,说明如何获取用于操作各种接口的token令牌的,当然,实际环境下,一般都会使用HTTPS协议获取数据了,演示代码如下所示。</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #0000ff;">string</span> appid = <span style="color: #800000;">"</span><span style="color: #800000;">myapi_123456</span><span style="color: #800000;">"</span><span style="color: #000000;">;
</span><span style="color: #0000ff;">string</span> appsecret = <span style="color: #800000;">"</span><span style="color: #800000;">mySecret_2856FB9DBE31</span><span style="color: #800000;">"</span><span style="color: #000000;">;
</span><span style="color: #008000;">//</span><span style="color: #008000;">使用API方式,需要在缓存里面设置特殊的信息</span>
<span style="color: #0000ff;">var</span> url = <span style="color: #800000;">"</span><span style="color: #800000;">http://localhost:9001/api/Auth/GetAccessToken</span><span style="color: #800000;">"</span> + GetSignatureUrl(appid, appsecret) + <span style="color: #800000;">"</span><span style="color: #800000;">&username=admin&password=</span><span style="color: #800000;">"</span><span style="color: #000000;">;
TokenResult result </span>= JsonHelper<TokenResult><span style="color: #000000;">.ConvertJson(url);
</span><span style="color: #0000ff;">if</span>(result == <span style="color: #0000ff;">null</span><span style="color: #000000;">)
{
MessageDxUtil.ShowError(</span><span style="color: #800000;">"</span><span style="color: #800000;">获取授权信息出错,请检查地址是否正确!</span><span style="color: #800000;">"</span><span style="color: #000000;">);
}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p> </p>
<p>由于Web API的调用,都是一种无状态方式的调用方式,我们通过token来传递我们的用户信息,这样我们只需要验证Token就可以了。JWT的令牌生成逻辑如下所示</p>
<p><img src="http://images0.cnblogs.com/blog2015/8867/201507/041143099993045.png" alt=""></p>
<p>令牌生成后,我们需要在Web API调用处理前,对令牌进行校验,确保令牌是正确有效的。</p>
<p> </p>
<p>除了令牌的规则,还有一个是加密签名的处理,加密签名需要客户端和服务器端约定相同的秘钥,一般由Web API统一分配,然后传输的时候,客户端使用应用ID即可。</p>
<p>加密签名在服务端(Web API端)的验证流程参考微信的接口的处理方式,处理逻辑如下所示。</p>
<p>1)检查timestamp 与系统时间是否相差在合理时间内,如10分钟。<br>2)将appSecret、timestamp、nonce三个参数进行字典序排序<br>3)将三个参数字符串拼接成一个字符串进行SHA1加密<br>4)加密后的字符串可与signature对比,若匹配则标识该次请求来源于某应用端,请求是合法的。</p>
<p> </p>
<h3>4、Web API基类设计分析</h3>
<p>上面介绍了一些Web API控制器的职能,一般情况下,我们设计一个架构,还需要考虑到基类对象之间的重用关系,尽可能把接口抽象到基类层面上去,减少子类的开发代码量,降低维护成本。</p>
<p>基于上面的目的,参考了我的Web开发框架对于MVC控制器的设计思路</p>
<p><img style="width: 867px;" src="http://images.cnitblog.com/blog/8867/201308/27120607-6ad84bc6809c4e0fa5d46150569ce385.png" alt=""></p>
<p>重新整理了Web API的控制器设计对象继承关系,如下所示:</p>
<p><img style="width: 867px;" src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925152731178-729280110.png" alt=""></p>
<p>我们关键的核心就是设计好<span style="color: #ff0000;"><strong>BusinessController<B, T></strong></span>这个基类,里面设计了大量的通用接口,包括常规的增删改查、分页等处理接口,那么子类继承过来就可以直接拥有这些接口了,多方便啊。</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925155916662-113368530.png" alt=""></p>
<p><img style="width: 867px;" src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925160136569-599589316.png" alt=""></p>
<p> </p>
<h3>5)Web API客户端(混合式Winform框架模块)的调用</h3>
<p>上面介绍了Web API服务端平台的架构设计思路,通过上面的整合,我们减轻了开发重复功能的增删改查等基础功能的控制器代码,把这些接口抽象到接口里面即可实现。</p>
<p>但是我们具体应该如何遵循统一接口层Facade层的约定,然后统一调用WebAPI层的接口,做到悄无声息的从不同的数据源里面获取数据,展示在客户端里面呢。</p>
<p>上面我们分析到,整个混合式Winform框架模块里面,设计方面考虑了数据的获取方面:包含了直接从数据库获取,从WCF服务获取,以及Web API层的数据获取三部分内容,当然还可以有更多的数据接入模式(如WebService等),设计效果如下所示。</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925160534975-451803597.png" alt=""></p>
<p>所有的数据接入,我们在Facade层都统一到接口里面,客户端的调用也统一到了CallerFactory<T>这个泛型工厂里面,我们根据配置的不同,从不同的模块里面加载,从而实现不同数据源的动态获取了。</p>
<p>如下逻辑就是CallerFactory<T>泛型工厂类的加载逻辑,如下所示:</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925161033428-1524737338.png" alt=""></p>
<p>为了实现简化客户端调用的封装,我们一般也把常规的通用操作封装一下,如下是我原先混合框架里面的设计思路,里面的封装都是通过***Caller的类来进行数据的访问的,这些类统一实现一定关系的集成封装。</p>
<p><img src="http://images.cnitblog.com/blog/8867/201304/11201859-da8bc43ac3a940fab9e3f984543fe11d.png" alt=""></p>
<p>为了简化说明调用接口的处理,这里把上面的关系进行了简化,并加入了Web API的调用封装类的处理,几种访问模式下的调用端封装继承关系,如下设计图所示。</p>
<p><img src="http://images2015.cnblogs.com/blog/8867/201509/8867-20150925162813725-1174476473.png" alt=""></p>
<p>最底层的几个DictDataCaller分别是不同访问方式下的接口调用封装类,对于Web API来说,它的访问代码就是如下所示。</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">bool</span> Delete(<span style="color: #0000ff;">string</span><span style="color: #000000;"> key)
{
</span><span style="color: #0000ff;">var</span> action = <span style="color: #800000;">"</span><span style="color: #800000;">Delete</span><span style="color: #800000;">"</span><span style="color: #000000;">;
</span><span style="color: #0000ff;">string</span> url = GetPostUrlWithToken(action) +<span style="color: #0000ff;">string</span>.Format(<span style="color: #800000;">"</span><span style="color: #800000;">&id={0}</span><span style="color: #800000;">"</span><span style="color: #000000;">, key);
CommonResult result </span>= JsonHelper<CommonResult><span style="color: #000000;">.ConvertJson(url);
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result.Success;
}
</span><span style="color: #0000ff;">public</span> List<DictDataInfo> FindByTypeID(<span style="color: #0000ff;">string</span><span style="color: #000000;"> dictTypeId)
{
</span><span style="color: #0000ff;">var</span> action = <span style="color: #800000;">"</span><span style="color: #800000;">FindByTypeID</span><span style="color: #800000;">"</span><span style="color: #000000;">;
</span><span style="color: #0000ff;">string</span> url = GetTokenUrl(action) + <span style="color: #0000ff;">string</span>.Format(<span style="color: #800000;">"</span><span style="color: #800000;">&dictTypeId={0}</span><span style="color: #800000;">"</span><span style="color: #000000;">, dictTypeId);
List</span><DictDataInfo> result = JsonHelper<List<DictDataInfo>><span style="color: #000000;">.ConvertJson(url);
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result;
}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p>第一个Delete函数是基类提供的,这里进行了重写,一般情况下,不需要处理就具备增删改分页等基础接口的调用封装了。</p>
<p>由于所有的实现类都实现继承了统一的Facade层的接口,那么统一调用也就是自然而然的事情了。所以在Winform界面里面,所有的调用都是使 用CallerFactory<T>进行了统一的处理,数据访问的不同不影响接口的处理, 三种方式的数据调用,统一都是下面的代码进行处理。</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #ff0000;"> DictDataInfo info = <strong>CallerFactory<IDictDataService>.Instance</strong>.FindByID(ID);
</span><span style="color: #0000ff;">if</span> (info != <span style="color: #0000ff;">null</span><span style="color: #000000;">)
{
SetInfo(info);
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">
{
</span><span style="color: #ff0000;">bool succeed = <strong>CallerFactory<IDictDataService>.Instance</strong>.Update(info, info.ID.ToString());
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> succeed;
}
</span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码"><img src="http://common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
</div>
<p> http://www.cnblogs.com/wuhuacong/p/4838220.html</p>
</div></div>
相关推荐
本示例中的"WebApi Winform托管"是一个结合了C#的Windows Forms(Winform)和ASP.NET Web API的技术实现,它允许在Winform应用内部托管和调用Web API服务。下面我们将深入探讨这个主题。 **WebAPI**是微软为构建...
在本项目中,"webapi 项目代码及winform下调用接口" 提供了一个完整的示例,展示如何在WinForm应用程序中调用WebAPI接口进行数据交互。 1. **WebAPI基础** - **RESTful原则**:WebAPI遵循REST(Representational ...
【标题】"WebAPI寄宿于WinForm"的实现是一个技术方案,它允许开发者在Windows Forms应用程序中直接运行和控制WebAPI服务,而无需依赖IIS(Internet Information Services)这样的传统Web服务器。这种方式的优点在于...
在IT行业中,C#是一种广泛使用的编程语言,尤其在开发Windows桌面应用时,WinForm是一个常用的框架。WebAPI则是.NET Framework或.NET Core中用于构建RESTful服务的技术,它允许开发者创建HTTP服务,可以被各种客户端...
在IT行业中,Windows Forms(WinForms)是一种常用的桌面应用程序开发框架,而WebAPI则是用于构建RESTful服务的ASP.NET框架。本案例主要涉及如何在WinForm应用中通过HttpClient调用使用OAuth2.0授权的WebAPI接口。...
C# WinForm开发框架是一种基于.NET Framework的桌面应用程序开发工具,它为开发者提供了一套完整的控件、设计模式和架构,使得创建具有丰富用户界面的Windows应用程序变得更加便捷和高效。这款框架尤其适合初学者,...
C#桌面程序 winform WPF集成内置WebApi C# 创建HTTP Web API服务,winform WPF项目创建HTTP WEB服务,不使用IIS业务 WPF WebApi 限权限访问 在维护旧的项目时,有时需要提供APP连接的需求,就要提供HTTP服务,...
webapi 项目平台,包含接口生成,及接口调用方法,项目代码清晰明了,非常值得初学者参考借鉴,更具体的开发步骤可以登入我的博客查看:http://www.cnblogs.com/gudaozi/p/8384734.html
C#,Winform,开发的的一个示例程序,可实现WebAPI客户端发起数据POST请求,可实现WebAPI服务器响应客户端的POST请求。 WebAPI客户端,可使用钉钉和企业微信的群机器人Webhook地址,直接发起POST,实现消息推送。
在本项目"WinForm_WebApi.rar"中,我们探讨了一个独特的方法,即如何使用C#的WinForm应用程序承载WebApi服务,而不需要通过传统的Internet Information Services (IIS)进行发布。这种技术允许开发者在本地环境中快速...
.Net Core WebApi+SignalR+winform实时通信,私人聊天系统。已有功能:添加好友,好友上线通知,切换好友聊天,登录,注册, 支持挂载远程服务器实现超级低配版qq的功能。支持局域网内互发,上课的时候可以偷偷聊天...
WPF自宿主作为Web服务器,托管Web Api,使用的是OWIN来实现自承载Web Api框架,集成了swagger接口文档,可以实现将一个Web API宿主到一个任意类型的应用程序,包括控制台、Winform、WPF、Windows Service等 ...
在Windows API中,"WinForm"是指Windows Forms,它是.NET Framework的一部分,用于构建基于Windows的桌面应用程序。Windows Forms提供了丰富的控件库、事件驱动编程模型以及与Windows API的紧密集成,让开发者能够...
在Winform快速开发框架中,这些层被合理地封装,使得开发者可以快速构建出稳定且功能丰富的应用程序。表示层主要处理用户交互,业务逻辑层负责处理业务规则和流程,而数据访问层则专注于与数据库的交互。 框架中的...
在.NET MVC框架中,开发人员经常需要通过WebApi接口处理文件上传操作,这在Web应用程序中是常见的功能,尤其在用户需要提交表单或者分享数据时。本篇将详细讲解如何在.NET MVC应用中调用WebApi接口实现文件上传。 ...
易开发:采用插件形式开发,只要另建项目,然后在数据库中加Form的名称就可以 易扩展:可以无限扩展,权限智能配置 性能好:客户端与服务端分离,采用WCF应用服务 注:附加数据库,修改Gaoen.sConsoleHost项目中...
在csdn一篇博客中看到文章Winform通用框架之权限管理系统,但是只有文章没有源码就是耍流氓。于是自己写了出来,可用于所有管理系统的开发框架。 软件的整体主页布局是用到了WeifenLuo.WinFormsUI.Docking;的可拖拉...
在开发Web应用时,API(应用程序接口)是服务器与客户端之间通信的重要桥梁。Web API通常由后端服务提供,允许前端应用获取数据或执行特定功能。然而,由于浏览器的同源策略限制,不同源的HTTP请求(即跨域请求)会...
三层架构是一种常见的软件设计模式,尤其在Windows桌面应用程序开发中,如WinForm程序,它将应用程序的业务逻辑、数据访问和用户界面分离,以提高代码的可维护性、可扩展性和可重用性。本项目是基于DotNetBar控件库...
标题中的“自己用EF写的winform权限框架源码”表明这是一个基于C#编程语言,采用Entity Framework(简称EF)作为数据访问层技术,用于Windows Forms(Winform)应用程序的权限控制框架。该框架的设计遵循了分层架构...