标签:ica 日志记录 nfa 抛出异常 方法 pat 学历 efault code
所有从服务端抛出的异常,只有类型为FaultException(或其子类)的异常才能直接被序列化并最终通过消息返回给客户端.FaultException允许将错误细节定义在一个对象上,而泛型参数TDetail表示这个对象的类型.
[Serializable]
public class FaultException<TDetail>:FaultException
{
//Else Member
public FaultException(TDetail detail);
public TDetail Detail {get;} //只读属性,表示指定的错误细节对象
}
不特殊指定,客户端捕获的异常类型实际上是FaultException. 也就是说,其具体的泛型参数为System.ServiceModel.ExceptionDetail.
[DataContract]
public class ExceptionDetail
{
//Else Member
public ExceptionDetail(Exception ex);
[DataMember]
public string HelpLink {get; private set;}
[DataMember]
public ExceptionDetail InnerException{get; private set;}
[DataMember]
public string Message {get;private set;}
[DataMember]
public string StackTrace {get; private set;}
[DataMember]
public string Type {get; private set;}
}
[DataContract(Namespace="http://www.xiaoxiao.com")]
public class CalculationError
{
public CalculationError(string operation,string message)
{
this.Operation=operation;
this.Message = message;
}
[DataMember]
public string Operation {get; set;}
[DataMembar]
public string Message {get; set;}
}
//Usage
var error=new CalcuationError("Divide","被除数y不能为0");
throw new FaultException<CalcuationError>(error,error.Message);
客户端能够捕获到服务端抛出的FaultException异常,并不是说客户端捕获的异常就是服务端抛出的异常. 这个过程经历了对异常的序列化和反序列化过程. 错误细节对象呗学历恶化为XML并通过SOAP消息的形式返回到客户端,客户端需要通过反序列化以重建错误细节对象.而反序列化的前提是需要知道其类型. 错误契约(FaultContract)帮助将作为错误细节对象的类型确定下来.
[AttributeUsage(AttributeTargets.Methord,AllowMultiple=true, Inherited=false)]
public sealed class FaultContractAttribute:Attribute
{
public FaultContractAttribute(Type detailType);
public string Action {get; set;} //错误消息<Action>报头的值.如果没有被显示指定,那么它将按照先的规则进行指定:{服务契命名空间}/{服务契约名称}/{操作契约名称}{细节类型名称}Fault;
public Type DetailType {get;} //用于封装错误信息的错误细节类型
public string Name {get; set;} //错误细节被学历序列成XML元素的名称, 未显示指定,将使用DetailType对应的数据契约名称
public string Namespace {get;set;} //命名空间 未显示指定,将使用DetailType对应的数据契约命名空间
public bool HasProtectionLevel {get;} //保护级别
public ProtectionLevel ProtectionLevel {get; set;} //保护级别
}
[ServiceContract(Namespace="http://www.xiaoxiao.com")]
public interface ICalculator
{
[OperationContract]
[FaultContract(typeof(CalculatorError))]
int Divide(int x, int y);
}
//usage
throw new FaultException<string>("y can‘t to be zero!"," the parameter y can‘t to be zero!");
[OperationContract]
[FaultContract(typeof(string))]
int Divide(int x, int y);
[OperationContract]
[FaultContract(typeof(CalcuatorError),Name="CalculationError")]
[XmlSerializerFormat(SupportFaults=true)]
int Divide(int x, int y);
由于在整个SOAP消息的路由过程中,错误可能发生在最终接收节点,也可能发生在中间节点,因此为了使SOAP错误消息的接收者能够判断导致错误的SOAP节点类型,在生成错误消息的时候,可以通过Node元素指定节点的类型.
WCF体系下,数据存在的形态大体分为两种:XML和托管对象.
//Message 转 MessageFault:
public class MessageFault
{
public static MessageFault CreateFault(Message message, int maxBufferSize)
}
//MessageFault 转 Message :
public class Message
{
public static Message CreateMessage(MessageVersion version, MessageFault fault, string action)
}
public class FaultException:CommunicationException
{
//Else
public static FaultException CreateFault(MessageFault messageFault,params Type[] faultDetailTypes);
public static FaultException CreateFault(MessageFault messageFault, string action, params Type[] faultDetailTypes);
public virtual MessageFault CreateMessageFault();
}
MessageFormater实现了在正常的服务调用过程中方法调用和消息之间的转换. 但是当异常(这里只FaultException)从服务端抛出后,WCF需要一个类似实现类似功能, 即在服务端对异常对象进行序列化并生成错误消息,在客户端对接收到的错误消息进行反序列化重建并抛出异常. 该使命由FaultFormatter担当
FaultFormatter在客户端和服务端所扮演的角色是不同的:
internal interface IClientFaultFormatter
{
FaultException Deserialize(MessageFault messageFault, string action);
}
internal interface IDispatchFaultFormatter
{
MessageFault Serialize(FaultException faultException, out string action);
}
internal class FaultFormatter:IClientFaultFormatter,IDispatchFaultFormatter
{
public FaultException Deserialize(MessageFault messageFault, string action);
public MessageFault Serialize(FaultException faultException, out string action);
}
如果服务操作在执行过程中抛出FaultException异常,WCF会获取当前分发操作的FaultFormatter,调用Serialize方法对异常对象进行序列化. 序列化完成后得到相应的MessageFault对象和Action值,这两个值最终通过调用Message的CreateMessage静态方法生成一个错误消息对象
客户端的服务调用最终通过客户端操作对象完成.当调用服务获得回复消息后,如果回复消息是错误消息,WCF会调用MessageFault的CreateFault将消息转换成MessageFault对象,并获取Action值. 最终通过客户端操作的得到FaultFormatter,传入MessageFault对象和Action值调用Deserialize方法在客户端重建FaultException异常对象并将其抛出来
对于应用了IncludeExceptionDetailInFaults属性的True的ServiceDebugBehavior服务行为,客户端是如何将错误消息中显示错误细节的XML反序列化成ExceptionDetail? 由于我们不曾通过FaultContractAttribute特性将ExceptionDetail类型应用在相应的操作方法中,因此FaultFormatter无法确定反序列化的对象类型。
原因是WCF在初始化FaultFormatter的时候会给予ExceptionDetail类型创建FaultContractInfo对象,并将其添加到属于自己的FaultContract列表中。
错误处理器实现了System.ServiceModel.Dispatcher.IErrorHandler
public interface IErrorHandler
{
bool HandlerException(Exception error);
}
void ProvideFault(Exception error, MessageVersion version, ref Message fault);
WCF运行时角度,错误处理器隶属于信道分发器,它维护着错误处理器列表。可以将多个错误处理器应用到相应的信道分发器上,这些分发器最终连成一个管道。每个错误处理器实现某个单一异常处理任务,如日志记录,敏感信息屏蔽等
public class ChannelDispatcher:ChannelDispatcherBase
{
//else
public Collection<IErrorHandler> ErrorHandlers {get;}
}
异常抛出后,WCF会遍历响应信道分发器的ErrorHandlers属性表示的错误处理集合,并调用错误处理器的ProvideFault方法.
对于Provider方法的执行,有一些值得注意的地方。该方法的执行发生在绘画终结之前,并且是在执行操作方法的线程中执行的。换句话说,ProviderFault方法是以与操作方法同步的方式执行的,所以在自定义ErrorHandler的时候,不应该将一些比较耗时的操作实现放在ProviderFault中。
标签:ica 日志记录 nfa 抛出异常 方法 pat 学历 efault code
原文地址:http://www.cnblogs.com/dfyg-xiaoxiao/p/6379274.html