# 跟我一起学WCF(3)——利用Web Services开发分布式应用
## 一、引言
在前面文章中分别介绍了MSMQ和.NET Remoting技术,今天继续分享.NET 平台下另一种分布式技术——Web Services
## 二、Web Services 详细介绍
## 2.1 Web Services 概述
Web Services是支持客户端与服务器通过网络互操作的一种软件系统,是一组可以通过网络调用的应用程序API。在Web Services中主要到SOAP/UDDI/WSDL这三个核心概念,下面分别介绍下这三个概念的定义。
* SOAP:SOAP(Simple Object Access Protocol,简单对象访问协议)是在分散或分布式的环境中交换信息的简单协议,是一种基于XML的协议,需要绑定一个网络传输协议来完成信息的传输,这个协议通常是Http或Https,但也可以使其他协议。
它包括四个部分:
SOAP封装:它定义了一个框架,描述消息中的内容是描述,是谁发送的,谁又应当接收并处理;
SOAP编码规则:定义了一种序列化的机制,用于表示应用程序需要使用的数据类型的实例;
SOAP RPC:表示一种协定,用于表示远程过程调用和应答;
SOAP绑定:它定义了SOAP使用哪种协议来进行交换信息。使用Http/TCP/UDP都可以。与WCF中的绑定概念一致。
换句话说,SOAP协议只是用来封装消息用的,封装后的消息你可以通过各种已有的协议来传输,如Http、Https、Tcp、UDP、SMTP等,甚至你还可以自定义协议。然而Web Service是采用基于Http协议来传输数据的。关于使用Https协议来访问Web Services的方法可以参考这个文章:[如何利用 SSL 调用 Web 服务](http://msdn.microsoft.com/zh-cn/library/aa302409.aspx)。
* UDDI:是统一描述、发现和集成(Universal Description, Discovery, and Integration)的缩写,它是一个基于XML的跨平台的描述规范,可以使世界范围内的企业在互联网上发布自己所提供的服务供其他客户查询使用。
* WSDL:是Web服务描述语言(Web Services Description Language),是为描述Web服务发布的XML格式。用于描述服务器端口访问方式和使用协议的细节,通常用来辅助生产服务器和客户端代码及配置信息。
## 2.2 Web Services 实现过程
调用Web Services的实现过程与进行常规方法调用过程类似。不同的在于,前者方法并不位于客户端应用程序中,而是通过指定传输协议生成请求消息。因为Web Services可能位于不同的计算机上,因此必须将Web Services处理请求所需的信息通过网络传递给含有Web Services的服务器,Web Services在处理信息后,会通过网络将结果发送回客户端应用程序。下图显示了客户端与Web Services之间的通信过程:
![XML Web services 生存期](https://box.kancloud.cn/2016-01-23_56a2eb4724588.gif)
下面介绍下调用Web Services时事件发生顺序:
1. 在客户端上,创建了一个Web Services代理类的实例。该对象驻留在客户端机器上。
2. 客户端调用代理类上的方法
3. 客户端机器将Web Services方法的参数序列化为SOAP消息,然后通过传送协议发送给Web Services。
4. Web Services底层结构接收SOAP消息并进行反序列化。它会创建Web Services的类的实例,同时调用对应的Web Services方法。
5. Web Services方法执行,并返回结果。
6. Web Services底层结构会将返回结果序列化为SOAP消息,然后通过网络发送回客户端。
7. 客户端将接收SOAP消息,然后将XML反序列为返回值或任何输出参数,并将它们传递给代理类的实例。
8. 客户端接收返回值和所有输出参数。
## 2.3 Web Services 优缺点
经过上面详细的介绍后,Web Services很明显具有以下优点:
* 跨平台:Web Services完全基于XML(可扩展标记语言)、XSD(XMLSchema)等与平台无关的行业标准。
* 自描述:Web Service使用WSDL进行自我描述,包括服务的方法、参数、类型和返回值等相关信息。
* 跨防火墙:Web Service使用http协议进行通信,可以穿越防火墙。
Web Services也具有以下缺点:
* 效率低下,不适合做单应用系统的开发。
* 安全问题:Web Services没有自身的安全机制,必须借助Http协议或IIS等宿主程序实现信息安全加密。
## 三、使用Web Services来开发分布式应用程序
使用Web Services来开发分布式应用较MSMQ和.NET Remoting来说相对简单很多,今天的示例程序分三步走:
1. 创建一个实现用户信息验证的项目WebServiceUserValidation。具体的实现代码如下所示:
```
1 namespace WebServiceUserValidation
2 {
3 public class UserValidation
4 {
5 // 判断用户名和密码是否有效
6 public static bool IsUserLegal(string name, string psw)
7 {
8 // 用户可以访问数据库进行用户和密码验证
9 // 这里仅仅作为演示
10 string password = "LearningHard";
11 if (string.Equals(password, psw))
12 {
13 return true;
14 }
15 else
16 {
17 return false;
18 }
19 }
20
21 // 判断用户的凭证是否有效
22 public static bool IsUserLegal(string token)
23 {
24 // 用户可以访问数据库进行用户凭证验证
25 // 这里只做演示
26 string password = "LearningHard";
27 if (string.Equals(password, token))
28 {
29 return true;
30 }
31 else
32 {
33 return false;
34 }
35 }
36 }
37 }
```
2\. 创建Web Services服务类,需要创建一个继承自[SoapHeader](http://msdn.microsoft.com/zh-cn/library/system.web.services.protocols.soapheader(v=vs.110).aspx),来接收SOAP 头里的消息,并添加WebServiceUserValidation程序集。通过添加Asp.net 空Web应用程序来创建Web Services服务工程,再右键创建的Web 应用程序工程添加一个Web 服务文件来创建Web 服务。具体的实现代码如下所示:
```
1 // 用户自定义的SoapHeader类必须继承于SoapHeader
2 public class MySoapHeader : SoapHeader
3 {
4 // 存储用户凭证
5 public string Token { get; set; }
6 }
7 /// <summary>
8 /// LearningHardWebService 的摘要说明
9 /// </summary>
10 [WebService(Namespace = "http://www.cnblogs.com/zhili/")]
11 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
12 [System.ComponentModel.ToolboxItem(false)]
13 // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。
14 // [System.Web.Script.Services.ScriptService]
15 public class LearningHardWebService : System.Web.Services.WebService
16 {
17 // 存储用户凭证的Soap Header信息
18 // 必须保证是public和字段名必须与SoapHeader("memberName")中memberName一样
19 // 否则会出现“头属性/字段 LearningHardWebService.authenticationToken 缺失或者不是公共的。”的异常
20 public MySoapHeader authenticationToken;
21 private const string TOKEN = "LearningHard"; // 存储服务器端凭证
22
23
24 // 定义SoapHeader传递的方向
25 //SoapHeaderDirection.In;只发送SoapHeader到服务端,该值是默认值
26 //SoapHeaderDirection.Out;只发送SoapHeader到客户端
27 //SoapHeaderDirection.InOut;发送SoapHeader到服务端和客户端
28 //SoapHeaderDirection.Fault;服务端方法异常的话,会发送异常信息到客户端
29 [SoapHeader("authenticationToken", Direction = SoapHeaderDirection.InOut)]
30 [WebMethod(EnableSession = false)]
31 public string HelloLearningHard()
32 {
33 if (authenticationToken != null && UserValidation.IsUserLegal(authenticationToken.Token))
34 {
35 return "LearningHard 你好,调用服务方法成功!";
36 }
37 else
38 {
39 throw new SoapException("身份验证失败", SoapException.ServerFaultCode);
40 }
41 }
42 }
```
在上面代码中需要注意的是,Web Servies中的Web方法需要抛出SoapExcetion异常才能被客户端捕获到,如果在Debug模式下调试运行的话,还需要在异常设置里把这个异常勾选掉,即编译器不对该异常进行捕获。
3\. 创建控制台客户端,通过添加服务引用的方式来添加Web Services,添加成功后,会在客户端程序中创建一个代理类,客户端可以通过该代理类来调用Web Services的方法,具体的实现代码如下所示:
```
1 namespace WebServiceClient
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 // 实例化一个Soap协议的头
8 MySoapHeader mySoapHeader = new MySoapHeader() { Token = "LearningHard"};
9 string sResult = string.Empty;
10 LearningHardWebServiceSoapClient learningHardWebSer = null;
11 try
12 {
13 // 实例化Web服务的客户端代理类
14 learningHardWebSer = new LearningHardWebServiceSoapClient();
15 // 调用Web服务上的方法
16 sResult= learningHardWebSer.HelloLearningHard(ref mySoapHeader);
17 // 输出结果
18 Console.WriteLine(sResult);
19 }
20 catch
21 {
22 Console.WriteLine("调用Web服务失败!");
23 }
24 finally
25 {
26 // 释放托管资源
27 if (learningHardWebSer != null)
28 {
29 learningHardWebSer.Close();
30 }
31 }
32
33 Console.WriteLine("请按任意键结束...");
34 Console.ReadLine();
35 }
36 }
37 }
```
关于Web Services异常捕获的更多信息可以参考MSDN:[在 XML Web services 中处理和引发异常](http://msdn.microsoft.com/zh-cn/library/ds492xtk(v=vs.100).aspx)。然而在这个MSDN上的示例代码好像运行不成功,后面发现,该文章中的客户端对异常的处理只处理了SoapException 异常,而此时客户端触发的异常时FaultException异常,所以异常处理代码应像下面代码一样处理,当然也可以直接只处理Exception异常,我上面代码就只处理这个大范围的异常。
```
catch (SoapException ex)
{
// Do sth with SoapException
}
catch (Exception ex)
{
// Do sth with Exception
}
```
经过上面的步骤,我们就已经完成了所有的开发工作,下面运行来测试下该程序的运行效果。把WebServiceClient作为启动项目,直接按F5或Ctrl+F5来运行客户端程序,你将看到如下所示的结果:
![](https://box.kancloud.cn/2016-01-23_56a2eb4733fba.png)
注:像一般分布式应用程序,都应用先运行服务器端,再运行客户端来访问服务方法。而这里我们运行却直接运行客户端就可以访问Web Services中的Web方法了。这是因为在运行Web Services客户端程序之前,会先把Web Services部署到IIS Express 中,你将会看到任务栏右下角有![](https://box.kancloud.cn/2016-01-23_56a2eb4745ba2.jpg),右键该图标就可以看到运行的Web Services。
## 四、总结
到这里,Web Services技术的分享就结束,从下一篇文章开始,将正式进入WCF的世界。而Web Services的内容和WCF内容一样也有很多,只是微软官方推荐采用WCF来创建Web服务程序,如果你想更多地了解Web Services的内容,可以参考MSDN:[使用 ASP.NET 创建的 XML Web Services 以及 XML Web Services 客户端](http://msdn.microsoft.com/zh-cn/library/7bkzywba(v=vs.100).aspx)
本文所有示例代码下载:[WebServiceSample](http://files.cnblogs.com/zhili/WebServiceSample.zip)
- C# 基础知识系列
- C# 基础知识系列 专题一:深入解析委托——C#中为什么要引入委托
- C# 基础知识系列 专题二:委托的本质论
- C# 基础知识系列 专题三:如何用委托包装多个方法——委托链
- C# 基础知识系列 专题四:事件揭秘
- C# 基础知识系列 专题五:当点击按钮时触发Click事件背后发生的事情
- C# 基础知识系列 专题六:泛型基础篇——为什么引入泛型
- C# 基础知识系列 专题七: 泛型深入理解(一)
- C# 基础知识系列 专题八: 深入理解泛型(二)
- C# 基础知识系列 专题九: 深入理解泛型可变性
- C#基础知识系列 专题十:全面解析可空类型
- C# 基础知识系列 专题十一:匿名方法解析
- C#基础知识系列 专题十二:迭代器
- C#基础知识 专题十三:全面解析对象集合初始化器、匿名类型和隐式类型
- C# 基础知识系列 专题十四:深入理解Lambda表达式
- C# 基础知识系列 专题十五:全面解析扩展方法
- C# 基础知识系列 专题十六:Linq介绍
- C#基础知识系列 专题十七:深入理解动态类型
- 你必须知道的异步编程 C# 5.0 新特性——Async和Await使异步编程更简单
- 全面解析C#中参数传递
- C#基础知识系列 全面解析C#中静态与非静态
- C# 基础知识系列 C#中易混淆的知识点
- C#进阶系列
- C#进阶系列 专题一:深入解析深拷贝和浅拷贝
- C#进阶系列 专题二:你知道Dictionary查找速度为什么快吗?
- C# 开发技巧系列
- C# 开发技巧系列 使用C#操作Word和Excel程序
- C# 开发技巧系列 使用C#操作幻灯片
- C# 开发技巧系列 如何动态设置屏幕分辨率
- C# 开发技巧系列 C#如何实现图片查看器
- C# 开发技巧 如何防止程序多次运行
- C# 开发技巧 实现属于自己的截图工具
- C# 开发技巧 如何使不符合要求的元素等于离它最近的一个元素
- C# 线程处理系列
- C# 线程处理系列 专题一:线程基础
- C# 线程处理系列 专题二:线程池中的工作者线程
- C# 线程处理系列 专题三:线程池中的I/O线程
- C# 线程处理系列 专题四:线程同步
- C# 线程处理系列 专题五:线程同步——事件构造
- C# 线程处理系列 专题六:线程同步——信号量和互斥体
- C# 多线程处理系列专题七——对多线程的补充
- C#网络编程系列
- C# 网络编程系列 专题一:网络协议简介
- C# 网络编程系列 专题二:HTTP协议详解
- C# 网络编程系列 专题三:自定义Web服务器
- C# 网络编程系列 专题四:自定义Web浏览器
- C# 网络编程系列 专题五:TCP编程
- C# 网络编程系列 专题六:UDP编程
- C# 网络编程系列 专题七:UDP编程补充——UDP广播程序的实现
- C# 网络编程系列 专题八:P2P编程
- C# 网络编程系列 专题九:实现类似QQ的即时通信程序
- C# 网络编程系列 专题十:实现简单的邮件收发器
- C# 网络编程系列 专题十一:实现一个基于FTP协议的程序——文件上传下载器
- C# 网络编程系列 专题十二:实现一个简单的FTP服务器
- C# 互操作性入门系列
- C# 互操作性入门系列(一):C#中互操作性介绍
- C# 互操作性入门系列(二):使用平台调用调用Win32 函数
- C# 互操作性入门系列(三):平台调用中的数据封送处理
- C# 互操作性入门系列(四):在C# 中调用COM组件
- CLR
- 谈谈: String 和StringBuilder区别和选择
- 谈谈:程序集加载和反射
- 利用反射获得委托和事件以及创建委托实例和添加事件处理程序
- 谈谈:.Net中的序列化和反序列化
- C#设计模式
- UML类图符号 各种关系说明以及举例
- C#设计模式(1)——单例模式
- C#设计模式(2)——简单工厂模式
- C#设计模式(3)——工厂方法模式
- C#设计模式(4)——抽象工厂模式
- C#设计模式(5)——建造者模式(Builder Pattern)
- C#设计模式(6)——原型模式(Prototype Pattern)
- C#设计模式(7)——适配器模式(Adapter Pattern)
- C#设计模式(8)——桥接模式(Bridge Pattern)
- C#设计模式(9)——装饰者模式(Decorator Pattern)
- C#设计模式(10)——组合模式(Composite Pattern)
- C#设计模式(11)——外观模式(Facade Pattern)
- C#设计模式(12)——享元模式(Flyweight Pattern)
- C#设计模式(13)——代理模式(Proxy Pattern)
- C#设计模式(14)——模板方法模式(Template Method)
- C#设计模式(15)——命令模式(Command Pattern)
- C#设计模式(16)——迭代器模式(Iterator Pattern)
- C#设计模式(17)——观察者模式(Observer Pattern)
- C#设计模式(18)——中介者模式(Mediator Pattern)
- C#设计模式(19)——状态者模式(State Pattern)
- C#设计模式(20)——策略者模式(Stragety Pattern)
- C#设计模式(21)——责任链模式
- C#设计模式(22)——访问者模式(Vistor Pattern)
- C#设计模式(23)——备忘录模式(Memento Pattern)
- C#设计模式总结
- WPF快速入门系列
- WPF快速入门系列(1)——WPF布局概览
- WPF快速入门系列(2)——深入解析依赖属性
- WPF快速入门系列(3)——深入解析WPF事件机制
- WPF快速入门系列(4)——深入解析WPF绑定
- WPF快速入门系列(5)——深入解析WPF命令
- WPF快速入门系列(6)——WPF资源和样式
- WPF快速入门系列(7)——深入解析WPF模板
- WPF快速入门系列(8)——MVVM快速入门
- WPF快速入门系列(9)——WPF任务管理工具实现
- ASP.NET 开发
- ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上
- ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理
- ASP.NET中实现回调
- 跟我一起学WCF
- 跟我一起学WCF(1)——MSMQ消息队列
- 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(4)——第一个WCF程序
- 跟我一起学WCF(5)——深入解析服务契约 上篇
- 跟我一起学WCF(6)——深入解析服务契约 下篇
- 跟我一起学WCF(7)——WCF数据契约与序列化详解
- 跟我一起学WCF(8)——WCF中Session、实例管理详解
- 跟我一起学WCF(9)——WCF回调操作的实现
- 跟我一起学WCF(10)——WCF中事务处理
- 跟我一起学WCF(11)——WCF中队列服务详解
- 跟我一起学WCF(12)——WCF中Rest服务入门
- 跟我一起学WCF(13)——WCF系列总结
- .NET领域驱动设计实战系列
- .NET领域驱动设计实战系列 专题一:前期准备之EF CodeFirst
- .NET领域驱动设计实战系列 专题二:结合领域驱动设计的面向服务架构来搭建网上书店
- .NET领域驱动设计实战系列 专题三:前期准备之规约模式(Specification Pattern)
- .NET领域驱动设计实战系列 专题四:前期准备之工作单元模式(Unit Of Work)
- .NET领域驱动设计实战系列 专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
- .NET领域驱动设计实战系列 专题六:DDD实践案例:网上书店订单功能的实现
- .NET领域驱动设计实战系列 专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
- .NET领域驱动设计实战系列 专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现
- .NET领域驱动设计实战系列 专题九:DDD案例:网上书店AOP和站点地图的实现
- .NET领域驱动设计实战系列 专题十:DDD扩展内容:全面剖析CQRS模式实现
- .NET领域驱动设计实战系列 专题十一:.NET 领域驱动设计实战系列总结