前往小程序,Get更优阅读体验!
立即前往
社区首页 >专栏 >WCF技术剖析之二十一:WCF基本异常处理模式[中篇]

WCF技术剖析之二十一:WCF基本异常处理模式[中篇]

作者头像
蒋金楠
发布于 2018-01-16 08:58:11
发布于 2018-01-16 08:58:11
1.3K0
举报
文章被收录于专栏:大内老A大内老A

通过WCF基本的异常处理模式[上篇], 我们知道了:在默认的情况下,服务端在执行某个服务操作时抛出的异常(在这里指非FaultException异常),其相关的错误信息仅仅限于服务端可见,并不会被WCF传递到客户端;如果将开启了IncludeExceptionDetailInFaults的ServiceDebug服务行为通过声明(通过在服务类型上应用ServiceBehaviorAttrite特性)或者配置的方式应用到相应的服务上,异常相关的所有细节信息将会原封不动地向客户端传送。

这两种方式体现了两种极端的异常传播(Exception Propagation)机制,对于基于服务操作执行过程中抛出的异常的错误细节,要么完全对客户端屏蔽,要么全部暴露于客户端。在真正通过WCF来架构我们的分布式系统中,我们往往需要一种折中的异常传播机制:自定义服务端异常信息。这样既可以让客户端得到一个易于理解的错误信息,又在一定程度上避免了一些敏感信息的泄露。

一、 通过FaultException直接指定错误信息

对于执行服务操作中抛出的异常,如果服务的定义者仅仅希望服务的调用者得到一段自定义的错误信息文本(字符串),我们要做的实际上很简单:在服务操作中直接抛出一个FaultException异常,该异常对象通过以字符串形式体现的自定义错误信息创建。下面的代码中,CalculaorService的Divide方式在指定的时候对第二参数进行了验证,如果为零则创建一个FaultException,并指定错误信息(“被除数y不能为零!”)。

代码语言:js
复制
   1: using System.ServiceModel;
   2: using Artech.WcfServices.Contracts;
   3: namespace Artech.WcfServices.Services
   4: {
   5:     [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
   6:     public class CalculatorService : ICalculator
   7:     {
   8:         public int Divide(int x, int y)
   9:         {
  10:             if (0 == y)
  11:             {
  12:                 throw new FaultException("被除数y不能为零!");
  13:             }
  14:             return x / y;
  15:         }
  16:     }
  17: }

客户端在调用该服务操作的时候,如果传入零作为被除数,将会直接捕获服务端定义的抛出的这个异常(实际上,这其中经历了异常对象的序列化、消息交换以及异常对象的反序列化等一系列的操作)。客户端具体的异常捕获情况如下面的程序体现:

代码语言:js
复制
   1: using System;
   2: using System.ServiceModel;
   3: using Artech.WcfServices.Contracts;
   4: namespace Artech.WcfServices.Clients
   5: {
   6:     class Program
   7:     {
   8:         static void Main(string[] args)
   9:         {
  10:             using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(
  11:                "calculatorservice"))
  12:             {
  13:                 ICalculator calculator = channelFactory.CreateChannel();
  14:                 using (calculator as IDisposable)
  15:                 {
  16:                     try
  17:                     {
  18:                         int result = calculator.Divide(1, 0);
  19:                     }
  20:                     catch (FaultException ex)
  21:                     {
  22:                         Console.WriteLine(ex.Message);
  23:                         (calculator as ICommunicationObject).Abort();
  24:                     }
  25:                 }
  26:             }
  27:         }
  28:     }
  29: }

输出结果:

被除数y不能为零!

虽然在很多情况下,在服务端指定服务操作的过程中直接抛出含有自定义错误信息的FaultException异常,就能过客户端感知到遇到的具体错误并进行必要的排错和纠错。但是,我们更多地,还是倾向于直接定义一个类型来描述异常信息。我个人倾向于这样一类的类型为错误明细类型(Fault Detail Type)。服务端根据具体的异常场景创建相应的错误类型对象,并基于该对象我们上面提到的System.ServiceModel.FaultException<TDetail>异常,其中泛型类型参数为异常细节类型。在这个过程中,还涉及到一个重要的概念:错误契约(Fault Contract),接下来,我们就来介绍一下FaultException<TDetail>和错误契约。

二、 通过FaultException<TDetail>采用自定义类型封装错误

由于用于封装错误信息的异常细节类型的对象最终需要通过消息交换的方式从服务端传播到客户端,所以该对象必须是一个可序列化的对象。WCF通过两种典型序列化器实现对数据对象的序列化和反序列化,其中一个是传统的System.Xml.Serialization.XmlSerializer,该序列换器被ASP.NET Web服务用于对象和XML之间的序列化和反序列化;另一个则是System.Runtime.Serialization.DataContractSerializer,用于基于数据契约对象的序列化和反序列化,后者是WCF默认采用的序列化器。所以,可序列化的错误明细类型一般是一个数据契约,或者是一个应用了System.SerializableAttribute特性的类型。关于序列化,和与此相关的数据契约、数据契约序列化器等,在《WCF技术剖析(卷1)》的第5章有深入、全面的介绍。

我们仍然用我们上面提到的计算服务来举例,现在我们需要定义一个独立的类型来描述基于CalculatorService的异常,我们索性将该类型起名为CalculationError。我们将CalculationError定义成一个应用了System.Runtime.Serialization.DataContractAttribute特性的数据契约,简单起见,我们仅仅定义了两个数据成员(应用了System.Runtime.Serialization.DataMemberAttribute特性):Operation表示导致异常相应的运算操作(我们假设CalculatorService具有一系列运算操作,虽然我们的例子中仅仅给出为一一个除法运算操作:Divide),而Message表述具体的错误消息。CalculationError的定义在被客户端(Client项目)和服务(Services项目)引用的契约(Contracts项目)中,具体定义如下:

代码语言:js
复制
   1: using System;
   2: using System.Runtime.Serialization;
   3: namespace Artech.WcfServices.Contracts
   4: {
   5:     [DataContractAttribute(Namespace="http://www.artech.com/")]
   6:     public class CalculationError
   7:     {
   8:         public CalculationError(string operation,string message)
   9:         {
  10:             if (string.IsNullOrEmpty(operation))
  11:             {
  12:                 throw new ArgumentNullException("operation");
  13:             }
  14:             if (string.IsNullOrEmpty(message))
  15:             {
  16:                 throw new ArgumentNullException("message");
  17:             }
  18:             this.Operation = operation;
  19:             this.Message = message;
  20:         }
  21:         [DataMember]
  22:         public string Operation
  23:         { get; set; }
  24:         [DataMember]
  25:         public string Message
  26:         { get; set; }
  27:     }
  28: }

照理说,我们已经正确定义了错误明细类型CalculationError,在CalculatorService的Divide操作中就可以直接抛出一个Fault<CalculationError>,并将一个创建一个CalculationError对象作为该异常对象的明细(通过Detail属性表示),具体的代码如下所示:

代码语言:js
复制
   1: using System.ServiceModel;
   2: using Artech.WcfServices.Contracts;
   3: namespace Artech.WcfServices.Services
   4: {
   5:     public class CalculatorService : ICalculator
   6:     {
   7:         public int Divide(int x, int y)
   8:         {
   9:             if (0 == y)
  10:             {
  11:                 var error = new CalculationError("Divide", "被除数y不能为零!");
  12:                 throw new FaultException<CalculationError>(error,error.Message);
  13:             }
  14:             return x / y;
  15:         }
  16:     }
  17: }

客户端服务调用相关的异常处理也作如下相应的修改:

代码语言:js
复制
   1: using System;
   2: using System.ServiceModel;
   3: using Artech.WcfServices.Contracts;
   4: namespace Artech.WcfServices.Clients
   5: {
   6:     class Program
   7:     {
   8:         static void Main(string[] args)
   9:         {
  10:             using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(
  11:                "calculatorservice"))
  12:             {
  13:                 ICalculator calculator = channelFactory.CreateChannel();
  14:                 using (calculator as IDisposable)
  15:                 {
  16:                     try
  17:                     {
  18:                         int result = calculator.Divide(1, 0);
  19:                     }
  20:                     catch (FaultException<CalculationError> ex)
  21:                     {
  22:                         Console.WriteLine("运算错误");
  23:                         Console.WriteLine("运算操作:{0}",ex.Detail.Operation);
  24:                         Console.WriteLine("错误消息:{0}",ex.Detail.Message);
  25:                         (calculator as ICommunicationObject).Abort();
  26:                     }
  27:                 }
  28:             }
  29:         }
  30:     }
  31: }

但是我们的客户端程序并不能按照我们实现预想的那样正常运行,而会抛出如图1所示的未被处理的FaultException异常,而我们试图捕获的异常类型为FaultException<CalculationError>。

图1 客户端不能正常捕获FaultException<CalculationError>异常

三、错误契约(Fault Contract)

要回答上面出错的原因,就需要谈到WCF或者SOA一个根本的特征:服务的提供者和消费者之间采用基于“契约(Contract)”的交互方式。不同于面向服务,在面向组件设计中,组件之间的交互实际上是基于类型的,交互双方需要共享相同类型集(接口、抽象类或者具体类等)。在《WCF技术剖析(卷1)》中,我们曾多次对契约进行过深入的探讨。从抽象层面上讲,契约时交互双方或者多方就某一问题达成的一种共识,使确保正常交互指定的一系列的规范。

从本质上讲,服务契约(Service Contract)中的每一个操作契约(Operation Contract),定义了WCF为实现该服务操作的调用采用的消息交换模式(MEP:Message Exchange Pattern),并且结合基于参数、返回值类型的数据契约、消息契约定义了请求消息和回复消息的结构(Schema)。数据契约建立了对相同数据的两种不同表现形式(托管对象和XML)之间的双向适配,以利于承载相同信息的数据在两种不同形态之间的转换,即序列换和反序列化。而消息契约在定义了托管对象的各个成员与相应的消息元素之间的匹配关系。借助于消息契约,在对一个托管对象进行序列化并生成消息的时候,可以有效地控制某一个数据成员(属性或者字段)被序列化成的XML应该置于消息报头(Header)还是消息主体(Body)。

总的来说,上述的这些契约基本上都是围绕着一个正常服务调用下的消息交换:服务的消费者通过向服务的提供者发送请求消息,服务的提供者在接受到该请求后,激活服务实例并调用相应的服务操作,最终将返回的结果以回复消息的方式返回给服务的消费者(对于One-way,则不需要消息的回复)。但是,如果服务操作不能正确地执行,服务端将会通过一种特殊的消息将错误信息返回给客户端,这种消息被称为错误消息(Fault Message)。对于错误消息,同样需要相应的契约来定义其结构,我们把这种契约称为错误契约(Fault Contract)。

WCF通过System.ServiceModel.FaultContractAttribute特性定义,由于错误契约是基于服务操作级别的,所以该特性直接应用于服务契约接口或者类的操作契约方法成员上。下面的代码片断体现了FaultContractAttribute的定义:

代码语言:js
复制
   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
   2: public sealed class FaultContractAttribute : Attribute
   3: {    
   4:     public FaultContractAttribute(Type detailType);
   5:     public string Action { get; set; }
   6:     public Type DetailType { get; }
   7:     public bool HasProtectionLevel { get; }
   8:     public string Name { get; set; }
   9:     public string Namespace { get; set; }
  10:     public ProtectionLevel ProtectionLevel { get; set; }
  11: }

FaultContractAttribute的6个属性分别具有如下的含义:

  • Action:和一般的SOAP消息一样,对于Fault SOAP,WS-Address报头Action是必须的,该属性控制Action报头的值。如果Action属性没有在应用FaultContractAttribute时显式指定,那么它将按照下面的规则进行指定:{服务契约命名空间}/{服务契约名称}/{操作契约名称}{明细类型名称}Fault;
  • DetailType:也就是上面所介绍的用于封装错误信息的错误明细类型,比如我们前面定义的CalculationError;
  • Name和Namespace:在最终的Fault SOAP中,错误明细对象被序列化成的XML将会被置于Fault SOAP的主体部分,而这两个属性则用于控制这段XML片断对应的名称和命名空间;如果这两个属性并未作显式设置,WCF将会使用DetailType对应的数据契约名称和命名空间;
  • HasProtectionLevel和ProtectionLevel:这两个属性涉及到保护级别,属于安全(Security)的问题,在这里就不多作介绍了。

下面的代码中,我们通过FaultContractAttribute将我们定义错误明细类型CalculationError应用到Divide操作之上,这样的话,我们的例子就能够正常运行了。

代码语言:js
复制
   1: using System.ServiceModel;
   2: namespace Artech.WcfServices.Contracts
   3: {
   4:     [ServiceContract(Namespace = "http://www.artech.com/")]
   5:     public interface ICalculator
   6:     {
   7:         [OperationContract]
   8:         [FaultContract(typeof(CalculationError))]
   9:         int Divide(int x, int y);
  10:     }   
  11: }

按照我们在上面提到的关于Action、Name和Namespace的默认设定,上面这段代码和下面的代码是完全等效的。

代码语言:js
复制
   1: using System.ServiceModel;
   2: namespace Artech.WcfServices.Contracts
   3: {
   4:     [ServiceContract(Name="ICalculator",Namespace = "http://www.artech.com/")]
   5:     public interface ICalculator
   6:     {
   7:         [OperationContract(Name="Divide")]
   8:         [FaultContract(typeof(CalculationError), 
   9:             Action = "http://www.artech.com/ICalculator/DivideCalculationErrorFault",
  10:             Name = "CalculationError", Namespace = "http://www.artech.com/")]
  11:         int Divide(int x, int y);
  12:     }  
  13: }

对于我们前面的例子,当客户端调用CalculatorService的Divide操作执行除法预算,并传入零作为被除数,服务端将会抛出FaultException<CalculationError>异常。WCF服务端框架将会产生一个Fault Message,并将序列化后的CalculationError对象作为错误明细放置到Fault Message的主体部分。如果采用的消息版本是Soap12Addressing10,即SOAP 1.2和WS-Addressing 1.0,最终生成的Fault Message将会如下面的XML片断所示:

代码语言:js
复制
   1: <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
   2:   <s:Header>
   3:     <a:Action s:mustUnderstand="1">http://www.artech.com/ICalculator/DivideCalculationErrorFault</a:Action>    <a:RelatesTo>urn:uuid:3498ba2d-edd0-4d3b-ba4a-9b35327b5fa3</a:RelatesTo>
   4:   </s:Header>
   5:   <s:Body>
   6:     <s:Fault>
   7:       <s:Code>
   8:         <s:Value>s:Sender</s:Value>
   9:       </s:Code>
  10:       <s:Reason>
  11:         <s:Text xml:lang="zh-CN">被除数y不能为零!</s:Text>
  12:       </s:Reason>
  13:       <s:Detail>
  14:         <CalculationError xmlns="http://www.artech.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  15:           <Message>被除数y不能为零!</Message>
  16:           <Operation>Divide</Operation>
  17:         </CalculationError>
  18:       </s:Detail>
  19:     </s:Fault>
  20:   </s:Body>
  21: </s:Envelope>

错误契约作为服务描述的一部分,会参与到描述服务的元数据(Metadata)中。当服务元数据通过WSDL的形式被发布的时候,作为对操作的描述的错误契约体现在WSDL的<wsdl:portType>/<wsdl:operation>/<wsdl:fault>节点。下面一段XML代表CalculatorService的WDSL:

代码语言:js
复制
   1: <?xml version="1.0" encoding="utf-8"?>
   2: <wsdl:definitions name="CalculatorService" targetNamespace="http://www.artech.com/" >
   3:   <wsdl:import namespace="http://tempuri.org/" location="http://127.0.0.1:3721/calculatorservice/mex?wsdl=wsdl0"/>
   4:   <wsdl:types>
   5:     <xs:schema elementFormDefault="qualified" targetNamespace="http://www.artech.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.artech.com/">
   6:       <xs:element name="Divide">
   7:         <xs:complexType>
   8:           <xs:sequence>
   9:             <xs:element minOccurs="0" name="x" type="xs:int"/>
  10:             <xs:element minOccurs="0" name="y" type="xs:int"/>
  11:           </xs:sequence>
  12:         </xs:complexType>
  13:       </xs:element>
  14:       <xs:element name="DivideResponse">
  15:         <xs:complexType>
  16:           <xs:sequence>
  17:             <xs:element minOccurs="0" name="DivideResult" type="xs:int"/>
  18:           </xs:sequence>
  19:         </xs:complexType>
  20:       </xs:element>
  21:       <xs:complexType name="CalculationError">
  22:         <xs:sequence>
  23:           <xs:element minOccurs="0" name="Message" nillable="true" type="xs:string"/>
  24:           <xs:element minOccurs="0" name="Operation" nillable="true" type="xs:string"/>
  25:         </xs:sequence>
  26:       </xs:complexType>
  27:       <xs:element name="CalculationError" nillable="true" type="tns:CalculationError"/>
  28:     </xs:schema>
  29:   </wsdl:types>
  30:   <wsdl:message name="ICalculator_Divide_InputMessage">
  31:     <wsdl:part name="parameters" element="tns:Divide"/>
  32:   </wsdl:message>
  33:   <wsdl:message name="ICalculator_Divide_OutputMessage">
  34:     <wsdl:part name="parameters" element="tns:DivideResponse"/>
  35:   </wsdl:message>
  36:   <wsdl:message name="ICalculator_Divide_CalculationErrorFault_FaultMessage">
  37:     <wsdl:part name="detail" element="tns:CalculationError"/>
  38:   </wsdl:message>
  39:   <wsdl:portType name="ICalculator">
  40:     <wsdl:operation name="Divide">
  41:       <wsdl:input wsaw:Action="http://www.artech.com/ICalculator/Divide" message="tns:ICalculator_Divide_InputMessage"/>
  42:       <wsdl:output wsaw:Action="http://www.artech.com/ICalculator/DivideResponse" message="tns:ICalculator_Divide_OutputMessage"/>
  43:       <wsdl:fault wsaw:Action="http://www.artech.com/ICalculator/DivideCalculationErrorFault" name="CalculationErrorFault" message="tns:ICalculator_Divide_CalculationErrorFault_FaultMessage"/>
  44:     </wsdl:operation>
  45:   </wsdl:portType>
  46:   <wsdl:service name="CalculatorService">
  47:     <wsdl:port name="CustomBinding_ICalculator" binding="i0:CustomBinding_ICalculator">
  48:       <soap12:address location="http://127.0.0.1:3721/calculatorservice"/>
  49:       <wsa10:EndpointReference>        <wsa10:Address>http://127.0.0.1:3721/calculatorservice</wsa10:Address>
  50:       </wsa10:EndpointReference>
  51:     </wsdl:port>
  52:   </wsdl:service>
  53: </wsdl:definitions>

对于错误契约的应用,还有一点需要特别说明:不仅仅是将自定义的错误明细类型(比如CalculationError)应用到相应的操作契约上,你需要显失地利用FaultContractAttribute特性将其应用到服务契约接口或者类中相应的操作方法上面,对于一些基元类型,比如Int32,String等,你也需要这样。也即是说,同样对于我们的计算服务的例子,如果服务端试图通过抛出一个FaultException<string>来提供错误(如下面的代码所示),客户端最后捕获到的仅仅是一个FaultException异常,而非FaultException<string>异常。

代码语言:js
复制
   1: using System.ServiceModel;
   2: using Artech.WcfServices.Contracts;
   3: namespace Artech.WcfServices.Services
   4: {
   5:     public class CalculatorService : ICalculator
   6:     {
   7:         public int Divide(int x, int y)
   8:         {
   9:             if (0 == y)
  10:             {
  11:                 throw new FaultException<string>("被除数y不能为零!", "被除数y不能为零!");
  12:             }
  13:             return x / y;
  14:         }
  15: }
  16: }

在这种情况下,你需要做的仍然是在相应的操作上面,通过应用FaultContractAttribute特性指定String类型作为其DetailType,如下面的代码所示:

代码语言:js
复制
   1: using System.ServiceModel;
   2: namespace Artech.WcfServices.Contracts
   3: {
   4:     [ServiceContract(Namespace = "http://www.artech.com/")]
   5:     public interface ICalculator
   6:     {
   7:         [OperationContract]
   8:         [FaultContract(typeof(string))]
   9:         int Divide(int x, int y);
  10:     }   
  11: }

从FaultContractAttribute的定义我们可以看出,该特性可以在同一个目标对象上面多次应用(AllowMultiple = true)。这也很好理解:对于同一个服务操作,可能具有不同的异常场景,在不同的情况下,需要抛出不同的异常。

代码语言:js
复制
   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
   2: public sealed class FaultContractAttribute : Attribute
   3: {    
   4:     //省略成员
   5: }

但是,如果你在同一个操作方法上面应用了多了FaultContractAttribute特性的时候,需要遵循一系列的规则,我们将在《WCF基本异常处理模式(下篇)》中进行逐条介绍。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2009-10-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
WCF技术剖析之二十一:WCF基本异常处理模式[下篇]
从FaultContractAttribute的定义我们可以看出,该特性可以在同一个目标对象上面多次应用(AllowMultiple = true)。这也很好理解:对于同一个服务操作,可能具有不同的异常场景,在不同的情况下,需要抛出不同的异常。 1: [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] 2: public sealed class FaultContractAttri
蒋金楠
2018/01/16
6190
WCF技术剖析之二十一:WCF基本异常处理模式[下篇]
WCF技术剖析之二十一: WCF基本的异常处理模式[上篇]
由于WCF采用.NET托管语言(C#和NET)作为其主要的编程语言,注定以了基于WCF的编程方式不可能很复杂。同时,WCF设计的一个目的就是提供基于非业务逻辑的通信实现,为编程人员提供一套简单易用的应用编程接口(API)。WCF编程模式的简单性同样体现在异常处理上面,本篇文章的主要目的就是对WCF基于异常处理的编程模式做一个简单的介绍。 一、当异常从服务端抛出 对于一个典型的WCF服务调用,我个人倾向于将潜在抛出的异常费为两种类型:应用异常(Application Exception)和基础结构(Infr
蒋金楠
2018/01/16
8020
WCF技术剖析之二十一: WCF基本的异常处理模式[上篇]
WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的?
服务端只有抛出FaultException异常才能被正常地序列化成Fault消息,并实现向客户端传播。对于一般的异常(比如执行Divide操作抛出的DivideByZeroException),在默认的情况下,异常信息无法实现向客户端传递。但是,倘若为某个服务应用了ServiceDebugBehavior这么一个服务行为,并开启了IncludeExceptionDetailInFaults开关,异常信息将会原封不动地传播到客户端。WCF内部是如何处理抛出的非FaultException异常的呢? 实际上
蒋金楠
2018/01/16
8580
Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[上]
在《WCF技术剖析(卷1)》的最后一章,我给出了一个具体的应用WCF的分布式应用实例,我把这个实例命名为PetShop。在这个例子中,我利用WCF的扩展实现了一些设计、架构模式,比如AOP、IoC等。看过本书的读者,一定还记得我还通过WCF扩展实现了于微软企业库(Enterprise Library)异常处理应用块(Exception Handling Application Block:EHAB)的集成。当时由于缺乏相应的背景知识,不可能介绍具体的实现,现在我们可以详细来讲述这是如何实现的。 (Sourc
蒋金楠
2018/01/16
5480
Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[上]
Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[下]
在上篇中,我详细介绍了如何通过自定义ClientMessageInspector和ErrorHandler,实现WCF与微软企业库中的Exception Handling Application Block(EHAB)之间的集成。这个方案的基本思路就是:当异常从服务端抛出,利用EHAB针对某个配置好的异常处理策略进行处理;然后将处理有的异常通过ServiceExceptionDetail对象进行封装,最后序列化置于Fault消息,最终被返回给客户端;客户端接收到该Fault消息后,提取并创建ServiceE
蒋金楠
2018/01/16
5020
WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]
在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常。在服务执行过程中,我们手工抛出FaultException异常,WCF服务端框架会对该异常对象进行序列化病最终生成Fault消息。当WCF客户端框架介绍到该Fault消息之后,会做一项相反的操作:对Fault消息中进行解析和反序列化,重新生成并抛出FaultException异常。WCF框架自动为我们作了这么多“幕后”工作,使得开发人员可以完全采用编写一般的.NET应用程序的模式进行异常的处理:在错误
蒋金楠
2018/01/16
1.5K0
WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]
WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[上篇]
对于上一篇文章 (WCF基本异常处理模式:[上篇]、[中篇]、[下篇]),主要是站在最终开发者的角度对WCF关于异常处理编程模式进行了介绍,接下来,我们需要将我们的目光转移到WCF框架内部,深入剖析整个WCF异常处理流程。在基于SOAP的消息交换过程中,异常最终通过Fault消息承载,所以很自然地,接下来的介绍从SOAP Fault说起。 一、 从SOAP Fault说起(基于SOAP 1.2) 服务调用的最终实现通过消息交换完成,WCF本质上可以看成是一个消息处理的框架。消息,不但承载着正常服务调用的请
蒋金楠
2018/01/16
1.2K0
我的WCF之旅(1):创建一个简单的WCF程序
为了使读者对基于WCF的编程模型有一个直观的映像,我将带领读者一步一步地创建一个完整的WCF应用。本应用功能虽然简单,但它涵盖了一个完整WCF应用的基本结构。对那些对WCF不是很了解的读者来说,这个例子将带领你正式进入WCF的世界。 在这个例子中,我们将实现一个简单的计算服务(CalculatorService),提供基本的加、减、乘、除的运算。和传统的分布式通信框架一样,WCF本质上提供一个跨进程、跨机器以致跨网络的服务调用。在本例中,客户端和服务通过运行在相同的同一台机器上不同进程模拟,图1体现了客户端
蒋金楠
2018/02/07
9180
我的WCF之旅(1):创建一个简单的WCF程序
我的WCF之旅(10):如何在WCF进行Exception Handling
在任何Application的开发中,对不可预知的异常进行troubleshooting时,异常处理显得尤为重要。对于一般的.NET系统来说,我们简单地借助try/catch可以很容易地实现这一功能。但是对于 一个分布式的环境来说,异常处理就没有那么简单了。按照面向服务的原则,我们把一些可复用的业务逻辑以Service的形式实现,各个Service处于一个自治的环境中,一个Service需要和另一个Service进行交互,只需要获得该Service的描述(Description)就可以了(比如WSDL,Sc
蒋金楠
2018/02/07
5520
我的WCF之旅(10):如何在WCF进行Exception Handling
[WCF权限控制]WCF自定义授权体系详解[实例篇]
在《原理篇》中,我们谈到WCF自定义授权体系具有两个核心的组件:AuthorizationPolicy和ServiceAuthorizationManager,已经它们是如何写作最终提供一种基于声明的授权实现。为了让自定义授权有深刻的理解,我们来进行一个简单实例来演示如何通过自定义这两个组件实现“非角色授权策略”。[源代码从这里下载] 目录: 一、创建演示程序解决方案 二、自定义AuthorizationPolicy 三、自定义ServiceAuthorizatio
蒋金楠
2018/01/16
9340
[WCF权限控制]WCF自定义授权体系详解[实例篇]
WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[中篇]
在[第1篇]中,我们介绍了WCF关于实例管理一些基本的知识点,包括InstanceContext、InstanceContextMode、已经如何通过ServiceBehaviorAttribute应用不同的实例上下文模式给不同的服务。在[第1篇]中,对WCF采用的三种不同实例上下文模式进行了简单的比较,本篇的重点方法对单调(PerCall)模式为进行详细介绍。 在单调(Per-Call)实例上下文模式下,WCF总是创建一个新的服务实例上下文处理接收到的每一个服务调用请求,并在服务操作执行结束后,回收服务上
蒋金楠
2018/01/16
8110
[WCF权限控制]基于Windows用户组的授权方式[下篇]
为了让读者对基于Windows用户组的授权具有深刻的认识,接下来我们通过一个简单的事例来讲解在真正的应用中该授权模式如何使用。对于接下来演示的事例,我们将采用Windows认证和授权。至于授权的最终实现,我们采用的是在服务方法上面应用PrincipalPermissionAttribute特性方式的声明式授权。[源代码从这里下载] 目录: 步骤一、创建测试帐号 步骤二、创建服务契约和服务 步骤三、寄宿服务 步骤四、创建客户端程序 步骤一、创建测试帐号 在创
蒋金楠
2018/01/16
9620
[WCF权限控制]基于Windows用户组的授权方式[下篇]
WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理
在前面一片文章(服务代理不能得到及时关闭会有什么后果?)中,我们谈到及时关闭服务代理(Service Proxy)在一个高并发环境下的重要意义,并阐明了其根本原因。但是,是否直接调用ICommunic
蒋金楠
2018/01/16
2K0
WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理
WCF技术剖析_学习笔记之一
本系列适合新手,从0学起。共同学习,共同探讨。 基础概念 SOA:就是采用Web服务的架构 它有一些特性,需要了解: 1、自治的:不依赖于访问它的客户端和其他服务,可以独立的进行部署和实施版本策略和安全策略。 2、依赖于开放的标准:让不同的厂商开发的服务能够进行互操作。 3、支持跨平台 4、鼓励创建可组合的服务 5、鼓励服务的复用 6、强调松耦合:契约的实现 WCF应用实例,帮助理解WCF服务的基本结构 过程: 1、构建解决方案 Contracts:定义服务的契约(接口部分) Services:定义服务的实
小端
2018/04/16
5280
WCF技术剖析_学习笔记之一
[WCF安全系列]消息的保护等级[下篇]
一、契约的保护等级为绑定进行消息保护设置了“最低标准” 二、显式地将保护等级设置成ProtectionLevel.None与没有设置保护等级有区别吗? 三、消息的保护等级与WS-Addressing 一、契约的保护等级为绑定进行消息保护设置了“最低标准” 定义在契约上消息保护级别实际上为WCF实施消息保护设置了一个“最低标准”。由于整个消息保护机制,不论是签名还是加密,都是在信道层实现的。而信道层最终是通过绑定来实现的,绑定的属性决定了信道层处理消息的能力。而绑定安全方面的
蒋金楠
2018/01/16
1.2K0
[WCF安全系列]消息的保护等级[下篇]
WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇]
在[第2篇]中,我们深入剖析了单调(PerCall)模式下WCF对服务实例生命周期的控制,现在我们来讨轮另一种极端的服务实例上下文模式:单例(Single)模式。在单例模式下,WCF通过创建一个唯一的服务实例来处理所有的客户端服务调用请求。这是一个极端的服务实例激活方式,由于服务实例的唯一性,所有客户端每次调用的状态能够被保存下来,但是当前的状态是所有客户端作用于服务实例的结果,而不能反映出具体某个客户端多次调用后的状态。WCF是一个典型的多线程的通信框架,对并发的服务调用请求是最基本的能力和要求,但是服务
蒋金楠
2018/01/16
1.3K0
[WCF安全系列]实例演示:TLS/SSL在WCF中的应用[SSL over TCP]
在接下来的系列文章中我们正是讨论关于身份认证的主题。在前面我们已经谈到了,WCF中的认证属于“双向认证”,既包括服务对客户端的认证(以下简称客户端认证),也包括客户端对服务的认证(以下简称服务认证)。客户端认证和服务认证从本质上并没有什么不同,无非都是被认证一方提供相应的用户凭证供对方对自己的身份进行验证。我们先来讨论服务认证,客户端认证放在后续的文章中。 在《从两种安全模式谈起》中,我们对TLS/SSL进行了简单的介绍。我们知道,客户端和服务在为建立安全上下文而进行的协商过程中会验证服务端的X.509证书
蒋金楠
2018/02/07
1.5K0
[WCF安全系列]实例演示:TLS/SSL在WCF中的应用[SSL over TCP]
WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
任何一个程序都需要运行于一个确定的进程中,进程是一个容器,其中包含程序实例运行所需的资源。同理,一个WCF服务的监听与执行同样需要通过一个进程来承载。我们将为WCF服务创建或指定一个进程的方式称为服务寄宿(Service Hosting)。服务寄宿的本质通过某种方式,创建或者指定一个进程用以监听服务的请求和执行服务操作,为服务提供一个运行环境。 服务寄宿的方式大体分两种:一种是为一组WCF服务创建一个托管的应用程序,通过手工启动程序的方式对服务进行寄宿,所有的托管的应用程序均可作为WCF服务的宿主,比如C
蒋金楠
2018/01/16
1K0
WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
在进行基于会话信道的WCF服务调用中,由于受到并发信道数量的限制,我们需要及时的关闭信道;当遇到某些异常,我们需要强行中止(Abort)信道,相关的原理,可以参考我的文章《服务代理不能得到及时关闭会有
蒋金楠
2018/01/16
2.1K0
通过“四大行为”对WCF的扩展[实例篇]
为了让读者对如何利用相应的行为对WCF进行扩展有个深刻的认识,在这里我提供一个简单的实例演示。本实例模拟的场景是这样的:我们创建一个支持多语言的资源服务,该服务旨在为调用者提供基于某种语言的文本型资源。但是,我们不希望客户端在每次调用服务的时候都显式地制定具体的语言,而是根据客户端服务调用线程表示语言文化的上下文来自动识别所需的语言。[源代码从这里下载] 要让资源服务具有识别语言文化的能够,我们必须将客户端服务调用线程当前的语言文化信息(具体来说就是Thread的两个属性:CurrentUICulture和
蒋金楠
2018/02/07
6070
通过“四大行为”对WCF的扩展[实例篇]
推荐阅读
WCF技术剖析之二十一:WCF基本异常处理模式[下篇]
6190
WCF技术剖析之二十一: WCF基本的异常处理模式[上篇]
8020
WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的?
8580
Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[上]
5480
Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[下]
5020
WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]
1.5K0
WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[上篇]
1.2K0
我的WCF之旅(1):创建一个简单的WCF程序
9180
我的WCF之旅(10):如何在WCF进行Exception Handling
5520
[WCF权限控制]WCF自定义授权体系详解[实例篇]
9340
WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[中篇]
8110
[WCF权限控制]基于Windows用户组的授权方式[下篇]
9620
WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理
2K0
WCF技术剖析_学习笔记之一
5280
[WCF安全系列]消息的保护等级[下篇]
1.2K0
WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇]
1.3K0
[WCF安全系列]实例演示:TLS/SSL在WCF中的应用[SSL over TCP]
1.5K0
WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
1K0
WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
2.1K0
通过“四大行为”对WCF的扩展[实例篇]
6070
相关推荐
WCF技术剖析之二十一:WCF基本异常处理模式[下篇]
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文