本文涉及源代码下载
简介:WCF的错误与异常处理是个令人头痛的问题,通常这些问题总是莫名其妙。错误的发生无非是来自服务端或者客户端。个人觉得,服务端发生的异常通常是不需要反馈给客户端的,除非特殊需要。服务端发生的异常通常在我们可控制的范围内,做为开发者本身,我么需要扑捉潜在的bug,这是我门迫切需要解决的问题但是又非朝夕之功,而对与不可抗力的异常,譬如断电,数据库连接失败,我门只需以友好的界面展示给客户就可以。而由客户端引起的异常才是我门真正努力的地方。譬如客户端非法操作或者合法操作但是出忽了我们的考虑引起的异常而往往更普遍,也更应该在我门受控范围之内。本文就是做这方面的简单阐述。另外,其实文章本身并不难,难于理解只是个人阐述水平的问题,还请阅读原代码。本文的思想和示例均来自Guy Burstein ,因重装了系统不能提供到原文连接,还请见凉。
内容:
一:介绍
二:验证WCF服务的消息和参数
三:在参数错误的时候返回异常
四:用验证行为扩展服务契约
五:WCF的扩展

介绍:
有许多方式去验证我门的服务操作的参数和要传递的消息。最普遍的方式去做这是在每个服务操作中写大量复杂的代码。尽管这个方法将帮助我们实现我们的目标,但是有时候它常常是太复杂而不益维护,并且也没有向配置文件那样有足够的弹性去控制。在许多额外的服务或者操作需要验证的时候,我门不得不一边又一边的做着同样的恼人的工作。
最有趣的事情是,在Enterprise Library3.0包含具有新特性的Validation Application Block,这让我门能够把业务对象的验证逻辑从业务逻辑中分离出来,并且能够以更多的弹性去配置它。
Validation Application Block能够无缝集成在我门开发的Windows Forms,Asp.net和Windows Communication Foundation等任何应用程序下。这篇文章将会讨论验证模块跟WCF的集成问题.
这篇文章假定你有一些对WCF的了解,并且你熟悉一些基本的验证模块的概念。
验证WCF服务的消息和参数
验证服务操作的参数
如果你的服务操作接受一些基本类型的参数,譬如:
[ServiceContract]
public interface IOrdersService
{
[OperationContract]
int CreateOrder(string currency, double amount);
}
并且你想要验证它们,你可以在服务契约中使用基于参数的验证。
[ServiceContract]
public interface IOrdersService
{
[OperationContract]
int CreateOrder(
[NotNullValidator] string currency,
[RangeValidator(1.0, RangeBoundaryType.Inclusive, 2.0,
RangeBoundaryType.Inclusive)] double amount);
}
请注意在服务操作的每一个参数前面的验证属性的用法。这些属性指定了参数验证的必须条件,只有表注了这些属性,Validation Application Block 才能在运行时给予验证。
验证传递给服务操作的消息契约或者数据契约.如果你的服务操作接受一个数据契约参数或者消息契约参数,你事实上可以利用基于契约的验证。譬如,如下的定单数据的数据契约有验证逻辑那确保Currency属性的值是预先定义的值。[DataContract]
public class OrderData
{
[DataMember]
public double Amount
{
get { return amount; }
set { amount = value; }
}
[DataMember]
[DomainValidator("USD","EUR","JPY")]
public string Currency
{
get { return currency; }
set { currency = value; }
}
}
现在,当你在一个服务契约中传递它的时候,你没有必要指定基于参数的验证,因为在传递数据契约的时候它从数据契约中进行验证。
[OperationContract]
OrderInfo CreateOrder(OrderData orderData);
为了验证一个业务对象,其实用属性并不是唯一的方法。如果你需要更多的弹性,你还可以通过配置文件完成同样的功能。不过这不在本文讨论范围内,可以参考相关资料。
用验证行为扩展服务契约
象我接下来详细讨论过后,对WCF的验证模块的扩展事实上是利用ValidationBehavior来实现的。一个WCF behavior实际上是一个组件,它控制着譬如一个Service,一个endpoint,一个operation,或者一个client的许多运行时定义,并且我门可以用属性或者配置文件来配置它。同样,这完全适合ValidationBehavior.
用属性去配置验证。
给一个服务添加验证行为的一个方法是在服务契约上添加[ValidationBehavior]属性。这个属性可以在每个服务操作被调用的时候,在服务操作的运行时添加一个ValidationParametersInspector.
[ServiceContract]
[ValidationBehavior]
public interface IOrdersService
{
...
}
用配置文件来配置验证。
为了配制对WCF的验证模块的扩展,首先你应该在System.ServiceModel声明the Validation Behavior Extension。
<configuration>
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="validation"
type="Microsoft.Practices.EnterpriseLibrary.
Validation.Integration.WCF.ValidationElement,
Microsoft.Practices.EnterpriseLibrary.Validation.
Integration.WCF, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
</configuration>
在配制文件的声明允许你配置一个端口行为。在我门例子,这是the ValidationElement,那有两个属性,enabled(true/false) and ruleset(string)。
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="Validation">
<validation enabled="true" ruleset="myruleset"/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
当用一个[ValidationBehavior]属性添加了一个基于服务水平的行为,用那个配置方法可以添加一个基于endpoint-level behavior.这样,如果你为一个服务暴露许多端口的时候,你可以有更多的验证规则,可以让一个endpoint具有验证行为,而另一个端口不具有这样的验证行为。
为了让一个端口具有验证行为,你需要添加behaviorConfiguration属性。
下面我展示一个完全的配置的场景。
<configuration>
<system.serviceModel>
<services>
<service name="Bursteg.Samples.WCFIntegration.Services.OrdersService"
behaviorConfiguration="PublishMetadata">

<endpoint address="http://localhost:8080/OrdersService"
binding="basicHttpBinding"
behaviorConfiguration="Validation"
contract="Bursteg.Samples.WCFIntegration.ServiceContracts.IOrdersService" />
</service>
</services>

<behaviors>
<endpointBehaviors>
<behavior name="Validation">
<validation enabled="true"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="PublishMetadata">
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8080/OrdersService/Mex"/>
</behavior>
</serviceBehaviors>
</behaviors>

<extensions>
<behaviorExtensions>
<add name="validation"
type="Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF.ValidationElement, Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
</configuration>
在参数错误的时候返回异常
那个Validation Application Block Inspector可以截取服务操作的调用,实现对传递给服务操作的参数的验证逻辑。但是如果传递给服务操作的参数被验证为不正确的然而实际上服务操作绝没有执行调用那么回发生什么或者说该怎样处理呢?实际上这也是本文谈论的一个核心。简单的来说,就是让验证模块抛出一个给客户端的异常,而服务操作却是透明的。
Validation Results.当用标准的代码去验证一个对象的时候,我通常用下面的代码。我门验证那个目标对象,接受回一个ValidationResults对象,那包含一些 Validation results.因此我门可以扑捉如此的结果,来处理异常。
Validator validator = ValidationFactory.CreateValidator<orderdata />();
ValidationResults results = validator.Validate(myOrderData);
foreach (ValidationResult result in results)
{
...
}
WCF是一些不同的,因为我门没有初始对对象的验证,而是由验证模块来完成的。如果参数发现是不正确的,这个组件会抛出一个FaultException<ValidationFault>,它能转化为一个适合传输的Soap Fault.那个ValidationFault类(相似的对于ValidationResults)包含了一系列的ValidationDetails项,那是相似和ValidationResult items,它包含了Validation message,key and tag.
WCF的错误处理是一个基本的话题,我门都是应该熟悉的,但是它不是本文章的一个话题。但是什么是重要的对于提级的是,如果你想要在客户端扑获异常和获取关于验证错误的细节,有两件事情你需要去做:
1。在每一个需要验证的服务操作的前面指定FaultContract属性,并且指定它的包含的类型为ValidationFault.
[ServiceContract]
public interface IOrdersService
{
[FaultContract(typeof(ValidationFault))]
[OperationContract]
OrderInfo CreateOrder(OrderData orderData);
}
2。用下面的代码扑捉异常和获取详细细节。
try
{
// Call the service operation
}
catch (FaultException<ValidationFault> ex)
{
// Extract the Detail node from the Fault Exception.
// This details is the
// ValidationFault class
ValidationFault fault = ex.Detail;
// Iterate through the list of validation errors
Console.WriteLine("Fault Occurred:");
foreach (ValidationDetail validationResult in faults.Details)
{
Console.WriteLine(string.Format("Message={0} Key={1} Tag={2}",
validationResult.Message, validationResult.Key,
validationResult.Tag));
}
}
WCF的扩展。
参数检查器。WCF在各个层次上有许多扩展点。你可以扩展Service Model,the Channel Layer,the Messaging layer and so on .本文,我门对一个参数检查器进行扩展,一个参数检查器能够截取来自客户端的或者服务端的传递来的或者发送的消息,并且能够对其进行一些检查或者说修改。为了实现一个参数检查器,我门必须实现IParameterInspector接口:
public interface IParameterInspector
{
void AfterCall(string operationName, object[] outputs,
object returnValue, object correlationState);
object BeforeCall(string operationName, object[] inputs);
}
操作描述。事实上,WCF是把操作,契约,端口的静态描述和他们的运行时定义是分开来的。只有当我门创造一个客户端或者服务端示例的时候,WCf才回构建他们的运行时定义,在这个构建过程中,所有的行为或者扩展才会被加入当中。
我门可以用以下的代码来获取对一个OperationDescription的引用,我么可以利用这个类来添加我门的扩展的参数检查器,这样在服务示例示例创建的时候,在运行时能够获得加入。
public OperationDescription GetOperationDescription(Type contractType,
string operationName)
{
ContractDescription contract = new ContractDescription(contractType.Name);
OperationDescription operation = new OperationDescription
(operationName, contract);
operation.SyncMethod = contractType.GetMethod(operationName);
return operation;
}
一旦我门获得了OperationDescription,我门可以创造一个ValidationParameterInspector的示例。
ValidationParameterInspector validationInspector =
new ValidationParameterInspector(operation, "myRuleset");
现在你可以测试你的服务操作的验证而没有必须使用客户端来传递消息。
try
{
// Create the data contract parameter to pass to the operation
OrderData orderData = new OrderData();
orderData.Amount = 3.0;
orderData.Currency = "ASD";
validationInspector.BeforeCall("CreateOrder", new object[] { orderData });
}
catch (FaultException<ValidationFault> e)
{
ValidationFault fault = e.Detail;
DisplayFault(fault);
}
不过我门应该知道的是,ValidatinParameterInspector仅仅实现了BeforeCall方法,(因为只有这样才是合情合理的),所以我么不应该测试AfterCall方法。
Validation Behavior.象我提级的在前边,我门可以用ValidationBehavior属性添加在服务契约的上边来通告WCF 运行时添加验证行为。这个行为实现了IEndpointBehavior 和IContractBehavior interfaces ,通过这,它把扩展应用在服务契约和端口描述的运行时上。对Validation Application Block来说,它添加了前边提级的Parameter Inspector.
这篇文章包含了一个例子,针对Validation Application Block Extensions for WCF.它展示了基于参数水平的和基于消息水平的验证,并且包含了验证逻辑必须的配置信息。为了理解我的阐述,请建议你详细阅读原代码。