WCF 异常与错误处理全解析
1. 异常与错误概述
在 WCF(Windows Communication Foundation)中,无论绑定配置如何,运行时都会使用相同的 CLR 类型来表示 SOAP 错误。大多数情况下,服务模型会根据绑定的正确 SOAP 版本来处理 SOAP 错误的序列化。
1.1 错误与 WSDL
在 WSDL 文档中描述的操作可以关联
<input>
、
<output>
或
<fault>
消息。错误消息和其他应用程序间交换的消息一样,由正式的契约描述。以下是不同类型的示例:
-
SOAP 1.1 错误示例
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns="">s:Client</faultcode>
<faultstring xml:lang="en-US" xmlns="">
An invalid operation has occurred.</faultstring>
</s:Fault>
</s:Body>
</s:Envelope>
- SOAP 1.2 错误示例
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">
http://www.w3.org/2005/08/addressing/soap/fault</a:Action>
<a:RelatesTo>urn:uuid:64c5619c-99c3-4a83-9bdc-fcbb6f399f93</a:RelatesTo>
</s:Header>
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Sender</s:Value>
</s:Code>
<s:Reason>
<s:Text xml:lang="en-US">An invalid operation has occurred.</s:Text>
</s:Reason>
</s:Fault>
</s:Body>
</s:Envelope>
- 定义了错误元素的 WSDL 操作示例
<wsdl:operation name="UploadFile">
<wsdl:input wsa:Action=
"http://www.thatindigogirl.com/samples/2005/12/FileUploadServices/UploadFile"
name="FileUploadRequest" message="tns:FileUploadRequest" />
<wsdl:output wsa:Action=
"http://www.thatindigogirl.com/samples/2005/12/FileUploadServices/
UploadFileResponse"
message="tns:FileUploadServicesContract_UploadFile_OutputMessage" />
<wsdl:fault wsa:Action=
"http://www.thatindigogirl.com/samples/2005/12/FileUploadServices/
FileUploadServicesContract/UploadFileExceptionFault" name="ExceptionFault"
message=
"tns:FileUploadServicesContract_UploadFile_ExceptionFault_FaultMessage" />
<wsdl:fault wsa:Action=
"http://www.thatindigogirl.com/samples/2005/12/FileUploadServices/
FileUploadServicesContract/UploadFileIOExceptionFault" name="IOExceptionFault"
message=
"tns:FileUploadServicesContract_UploadFile_IOExceptionFault_FaultMessage" />
</wsdl:operation>
1.2 WCF 异常处理
WCF 引入了新的异常处理概念,以解决传统面向组件系统和 SOA 之间的差异。传统面向组件系统可共享本地类型进行异常报告和处理,但在服务边界间此方法不适用。服务和客户端之间的异常会序列化为 SOAP 错误,服务模型可自动将异常转换为错误,也可使用 WCF 引入的新 CLR 类型显式创建错误。
1.2.1 错误异常
WCF 引入了几种新的异常类型,大多数派生自
CommunicationException
基类。其中,
FaultException
最为重要,二者都来自
System.ServiceModel
命名空间。
-
FaultException
属性与 SOAP 错误密切相关,包括
Action
、
Code
和
Reason
,还可通过
Message
属性访问与 SOAP 错误关联的整个消息。
- 还有泛型版本的
FaultException<T>
,可通过
Detail
属性强类型访问错误的详细元素。
以下是异常层次结构的 mermaid 流程图:
graph TD;
Exception --> SystemException;
SystemException --> CommunicationException;
CommunicationException --> FaultException;
FaultException --> FaultException<T>;
创建错误的技术包括:
- 构造
FaultException
的新实例,可提供原因和代码。
- 调用
FaultException
公开的静态
CreateFault()
方法,还可提供错误的详细部分。
- 构造带有强类型详细部分的
FaultException<T>
。
FaultException
最终用于生成
MessageFault
,它是 SOAP 错误的运行时表示。
MessageFault
是一个抽象类,可重写以控制 SOAP 错误的序列化。
1.3 服务异常处理
服务向客户端报告异常有以下几种选择:
- 允许异常传播到服务模型,这会导致服务模型使通道出错,并向客户端返回不太有意义的错误。
- 捕获异常并显式向服务模型抛出错误,这不会使通道出错,并能向客户端返回更好的信息,尤其是使用
FaultException<T>
时。
- 为每个操作声明已知错误,使信息成为服务描述的一部分,让客户端了解可能抛出的错误类型。
- 全局捕获异常并决定哪些异常应抛出错误而非未捕获的异常。
1.4 客户端异常处理
客户端应始终将服务调用包装在
try...catch
块中,以防通信异常。每次调用后,客户端应检查通道状态,若通道出错,需重新创建代理才能再次调用服务。客户端是否通知用户问题取决于具体应用。
常见的客户端需要处理的异常类型如下表所示:
| 异常类型 | 说明 |
| ---- | ---- |
|
CommunicationException
| WCF 中异常类型的基类型,捕获此异常可捕获大多数与服务模型通信相关的异常 |
|
EndpointNotFoundException
| 服务端点无法找到时抛出,通常是地址不正确 |
|
TimeoutException
| 服务操作运行时间超过绑定的接收超时时间,或客户端在配置的发送超时时间内无法到达远程服务时抛出 |
|
CommunicationObjectFaultedException
| 尝试在基础通道出错后使用代理时抛出 |
|
ProtocolException
| 客户端和服务绑定不兼容时,由服务模型抛出 |
|
MessageSecurityException
| 评估消息的安全设置有问题,或安全会话过期后客户端尝试在过期会话中发送消息时抛出 |
2. 异常与调试
2.1 未捕获异常的处理
当 .NET 运行时抛出异常且在调用链中未被捕获时,执行线程将中止。对于正在执行的服务操作,未捕获的异常会传播到服务模型,服务模型将未捕获的异常视为严重问题的潜在指标,会使通道进入出错状态。若执行的操作不是单向的,服务模型会向客户端返回错误,错误中的信息受异常类型和服务调试行为影响。
2.2 操作实验:处理未捕获异常
以下是实验的操作步骤:
1. 打开位于
<YourLearningWCFPath>\Labs\Chapter8\Exceptions\Exceptions.sln
的启动解决方案,该解决方案包含多个项目,
PhotoManagerService
用于上传图像,
Host
为服务暴露端点,
PhotoUploadClient
调用服务。
2. 安装数据库脚本
<YourLearningWCFPath>\SupportingFiles\DatabaseScripts\Photos_CreateDB.sql
。
3. 编译解决方案并运行文件上传测试,先运行
Host
,再运行
PhotoUploadClient
。
4. 从
PhotoUploadClient
应用程序尝试上传文件,选择示例图像
<YourLearningWCFPath>\SupportingFiles\SampleImages\theband.jpg
并点击上传,由于
Host
的
app.config
文件中指定的目录无效,服务将抛出
ConfigurationErrorsException
,客户端会收到
FaultException
,异常中的信息与原始异常无关。
5. 打开
Host
项目的
app.config
文件,添加
<serviceDebug>
元素并将
includeExceptionDetailInFaults
设置为
true
。
<behavior name="serviceBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
-
再次测试解决方案,此时客户端会收到
FaultException<T>,消息为原始ConfigurationErrorsException抛出的正确消息。
2.3 调试行为
默认情况下,服务模型向客户端返回错误时不返回未捕获异常的详细信息,因为这可能存在安全风险。开发期间,异常包含对开发者有用的信息,因此 WCF 提供了可选的服务调试行为,可在配置或代码中启用。
-
配置方式
<behavior name="serviceBehavior">
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
- 代码方式
[ServiceBehavior(IncludeExceptionDetailInFaults=true)]
public class PhotoManagerService : IPhotoManagerService
-
通过
ServiceHost初始化
using (ServiceHost host = new ServiceHost(typeof(PhotoManagerService))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService), new
BasicHttpBinding( ), "HelloIndigoService");
ServiceDebugBehavior debugBehavior =
host.Description.Behaviors.Find<ServiceDebugBehavior>( );
if (debugBehavior == null)
{
debugBehavior = new ServiceDebugBehavior( );
host.Description.Behaviors.Add(debugBehavior);
}
debugBehavior.IncludeExceptionDetailInFaults = true;
host.Open( );
Console.WriteLine("Press <ENTER> to terminate the service host");
Console.ReadLine( );
}
服务模型会对代码和配置中的设置进行异或运算以确定最终设置。
2.4 未捕获异常与 SOAP 错误
默认情况下,服务抛出未捕获异常时,服务模型会抛出错误并使服务通道出错。客户端收到的消息始终包含 SOAP 错误。服务调试禁用和启用时的 SOAP 错误示例如下:
服务调试禁用时的 SOAP 错误
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">
http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/
dispatcher/fault</a:Action>
<a:RelatesTo>urn:uuid:09013cb0-71fe-4861-8069-3d5e89f253a4</a:RelatesTo>
<a:To s:mustUnderstand="1">
http://www.w3.org/2005/08/addressing/anonymous</a:To>
</s:Header>
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Receiver</s:Value>
<s:Subcode>
<s:Value xmlns:a="http://schemas.microsoft.com/net/2005/12/
windowscommunicationfoundation/dispatcher">a:InternalServiceFault</s:Value>
</s:Subcode>
</s:Code>
<s:Reason>
<s:Text xml:lang="en-US">The server was unable to process the request due
to an internal error. For more information about the error, either turn on
IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the
<serviceDebug> configuration behavior) on the server in order to send the exception
information back to the client, or turn on tracing as per the Microsoft .NET
Framework 3.0 SDK documentation and inspect the server trace logs.</s:Text>
</s:Reason>
</s:Fault>
</s:Body>
</s:Envelope>
服务调试启用时的 SOAP 错误
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">
http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/
dispatcher/fault</a:Action>
<a:RelatesTo>urn:uuid:ce1f3861-6a5c-463f-aaee-eadec43466ae</a:RelatesTo>
<a:To s:mustUnderstand="1">
http://www.w3.org/2005/08/addressing/anonymous</a:To>
</s:Header>
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Receiver</s:Value>
<s:Subcode>
<s:Value xmlns:a="http://schemas.microsoft.com/net/2005/12/
windowscommunicationfoundation/dispatcher">a:InternalServiceFault</s:Value>
</s:Subcode>
</s:Code>
<s:Reason>
<s:Text xml:lang="en-US">Application setting is invalid:
fileUploadDir</s:Text>
</s:Reason>
<s:Detail>
<ExceptionDetail
xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<HelpLink i:nil="true"></HelpLink>
<InnerException i:nil="true"></InnerException>
<Message>Application setting is invalid: fileUploadDir</Message>
<StackTrace>
at FileManager.FileUploadUtil.SaveFile(String filename, Byte[] data) in
D:\LearningWCF\Common\FileManager\FileManager.cs:line 28
at PhotoManager.PhotoUploadUtil.SavePhoto(PhotoLink fileInfo, Byte[] fileData) in
D:\LearningWCF\Labs\Chapter8\Exceptions - Copy\PhotoManager\PhotoManager.cs:line 25
at PhotoManagerService.PhotoManagerService.UploadPhoto(PhotoLink fileInfo, Byte[]
fileData) in D:\LearningWCF\Labs\Chapter8\Exceptions –
Copy\PhotoManagerService\PhotoManagerService.cs:line 20
at SyncInvokeUploadPhoto(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance,
Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&
rpc) at System.ServiceModel.Dispatcher.
ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.
ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.
ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.
ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.
ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
</StackTrace>
<Type>System.Configuration.ConfigurationErrorsException</Type>
</ExceptionDetail>
</s:Detail>
</s:Fault>
</s:Body>
</s:Envelope>
通过以上内容,我们详细了解了 WCF 中的异常与错误处理,包括异常类型、服务和客户端的处理方式以及调试行为等方面的知识。在实际开发中,合理运用这些技术可以更好地处理异常情况,提高系统的稳定性和可靠性。
3. 异常处理的最佳实践
3.1 服务端最佳实践
-
明确声明已知错误
:为每个操作声明已知的错误类型,这样客户端在调用服务时就能提前了解可能出现的异常情况。例如在文件上传服务中,明确声明
ExceptionFault和IOExceptionFault,让客户端可以针对性地处理这些异常。 -
避免直接抛出通用异常
:尽量避免让异常直接传播到服务模型,因为这可能会导致通道出错并返回不太有意义的错误信息。应该捕获异常并显式抛出
FaultException或FaultException<T>,提供更详细的错误信息给客户端。 -
合理使用调试行为
:在开发阶段,可以启用服务调试行为,将
includeExceptionDetailInFaults设置为true,方便开发人员快速定位和解决问题。但在生产环境中,要将其关闭,避免泄露系统的敏感信息。
3.2 客户端最佳实践
-
全面捕获异常
:客户端应该捕获常见的异常类型,如
CommunicationException、EndpointNotFoundException等,并根据不同的异常类型采取相应的处理措施。例如,当捕获到EndpointNotFoundException时,可以提示用户检查服务地址是否正确。 - 检查通道状态 :在每次调用服务后,检查通道的状态。如果通道处于出错状态,需要重新创建代理后再进行后续的服务调用,以确保通信的正常进行。
-
处理自定义错误
:对于服务端声明的自定义错误,客户端应该能够识别并处理。通过
FaultException<T>可以获取到强类型的错误详细信息,方便进行针对性的处理。
3.3 异常处理的流程优化
以下是一个优化后的异常处理流程的 mermaid 流程图:
graph LR;
A[客户端调用服务] --> B{是否捕获到异常};
B -- 是 --> C{异常类型};
C -- CommunicationException --> D[检查通道状态并处理];
C -- EndpointNotFoundException --> E[提示用户检查地址];
C -- TimeoutException --> F[重试或提示用户];
C -- 其他 --> G[通用异常处理];
B -- 否 --> H[正常处理返回结果];
4. 总结与建议
4.1 总结
本文详细介绍了 WCF 中的异常与错误处理机制,包括异常类型、服务端和客户端的异常处理方式、调试行为以及最佳实践等内容。通过合理运用
FaultException
和
FaultException<T>
等类型,可以更好地向客户端传递错误信息;在服务端和客户端分别采取合适的异常处理策略,能够提高系统的稳定性和可靠性;同时,正确使用服务调试行为可以在开发阶段快速定位问题,在生产环境中保障系统的安全性。
4.2 建议
- 持续学习 :WCF 是一个功能强大且复杂的框架,异常处理只是其中的一部分。建议开发者持续学习 WCF 的其他特性和功能,不断提升自己的技术水平。
- 测试与验证 :在开发过程中,要对异常处理机制进行充分的测试和验证,确保在各种异常情况下系统都能正常运行。可以编写单元测试和集成测试,模拟不同的异常场景进行测试。
- 文档记录 :对于服务端声明的自定义错误和异常处理逻辑,要进行详细的文档记录。这样不仅方便团队成员之间的沟通和协作,也有助于后续的维护和扩展。
以下是一个异常处理相关的总结表格:
| 方面 | 要点 |
| ---- | ---- |
| 异常类型 |
CommunicationException
、
FaultException
、
FaultException<T>
等 |
| 服务端处理 | 声明已知错误、捕获异常并显式抛出、合理使用调试行为 |
| 客户端处理 | 捕获常见异常、检查通道状态、处理自定义错误 |
| 最佳实践 | 服务端和客户端分别遵循相应的最佳实践,优化异常处理流程 |
通过以上的总结和建议,希望开发者能够更好地掌握 WCF 中的异常与错误处理技术,在实际项目中运用这些知识提高系统的质量和性能。
超级会员免费看


被折叠的 条评论
为什么被折叠?



