[WCF 4.0新特性] 默认绑定和行为配置
对于传统的WCF配置系统,无论是绑定的配置还是行为(服务行为和终结点行为)都必须具有一个名称。而正是通过整个配置名称,它们才能被应用到目标对象(终结点或者服务)上。而在实际的项目开发中,绝大部分服务或者终结点都具有相同的绑定和行为,如果能够定义一种默认的绑定和行为,这无疑会简化我们的配置。WCF4.0为此提供了一个新的特性以支持默认绑定和行为的配置。
一、 默认绑定配置
在传统的配置方式下,如果我们需要对终结点的绑定(不论是系统绑定还是自定义绑定)进行定制,我们都需要配置一个“具名”的绑定,然后将这个名称指定为终结点配置节的bindingConfiguration属性进而将绑定配置应用到终结点绑定上。
比如说我需要采用WS2007HttpBinding作为终结点绑定,并且需要采用Message安全模式和用户名密码认证,我需要按照下面XML片断所示的方式进行配置。首先需要在<bindings>/<ws2007HttpBinding>结点下定义一个具体的WS2007HttpBinding,除了进行我们所需的安全相关配置之外,这个配置的绑定必须具有一个名字(defaultBinding)。然后将绑定的配置名称指定为终结点的配置属性bindingConfiguration,这就意味着终结点采用了配置的绑定。
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <bindings>
5: <ws2007HttpBinding>
6: <binding name="defaultBinding">
7: <security mode="Message">
8: <message clientCredentialType="UserName" />
9: </security>
10: </binding>
11: </ws2007HttpBinding>
12: </bindings>
13: <services>
14: <service name="Artech.NewFeaturesInWcf4.Service.CalculatorService">
15: <endpoint address="http://127.0.0.1:3721/calculatorservice" binding="ws2007HttpBinding"
16: bindingConfiguration="defaultBinding" contract="Artech.NewFeaturesInWcf4.Contract.ICalculator" />
17: </service>
18: </services>
19: </system.serviceModel>
20: </configuration>
也就是说,对于WCF传统的配置系统,并不存在一个“默认绑定”的概念。或者说所谓的“默认绑定”就是所有属性值均采用默认值的绑定,我们并不能显式地设置一个默认绑定。但是在具体的开发场景中,这样的需求是很常见的,因为在同一个应用里面绝大部分采用的绑定都具有相同的配置。为了解决这样的问题,最新的WCF支持对于默认绑定的配置。
在最新的WCF中,配置的绑定具有两种类型,一种是传统的具名绑定,也就是需要显式指定一个名称。另一种叫做默认绑定,默认绑定不要指定名称。对于上面给出的针对WSHttpBinding的配置,如果我们将其配置属性name去掉,它就成为了一个默认的绑定。对于所有将WSHttpBinding作为绑定的终结点,如果并没有对bindingConfiguration配置属性进行显式设置,这个默认绑定的所有配置就自动应用到了这些终结点的绑定上。
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <bindings>
5: <ws2007HttpBinding>
6: <binding>
7: <security mode="Message">
8: <message clientCredentialType="UserName" />
9: </security>
10: </binding>
11: </ws2007HttpBinding>
12: </bindings>
13: ...
14: </configuration>
二、默认行为配置
默认行为配置和默认绑定配置的作用类似,它允许我们在配置中定义不具名的服务行为或者终结点行为。不过在介绍默认行为配置之前,我们还是来介绍传统的服务行为和终结点行为采用怎样的配置方式。
在传统的配置系统下,无论是服务行为还是终结点行为,我们都必须为之指定一个名称。而服务和终结点的配置节都具有一个behaviorConfiguration配置属性,该属性用以设置配置的行为名称。正是通过这么一个配置属性,配置的服务行为能够应用到目标服务上,而目标终结点也能够使用配置的终结点行为。
比如在下面的一段配置中,我们配置两个名称为defaultBehavior的行为。其中一个为终结点行为,它实际上应用了ServiceDebugBehavior行为并将IncludeExceptionDetailInFaults设置为True,这样可以使服务端抛出的异常的详细信息通过错误消息传播到客户端以利于查错和纠错。另一个为服务行为,该行为将默认使用的DataContractSerializer的MaxItemsInObjectGraph属性设置为最大值,以实现对大数据对象序列化和反序列化。这两个行为最终被分别应用到了寄宿的CalculatorService服务以及它的终结点上。
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <endpointBehaviors>
6: <behavior name="defaultBehavior">
7: <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
8: </behavior>
9: </endpointBehaviors>
10: <serviceBehaviors>
11: <behavior name="defaultBehavior">
12: <serviceDebug includeExceptionDetailInFaults="true"/>
13: </behavior>
14: </serviceBehaviors>
15: </behaviors>
16: <services>
17: <service name="Artech.NewFeaturesInWcf4.Service.CalculatorService" behaviorConfiguration="defaultBehavior">
18: <endpoint address="http://127.0.0.1:3721/calculatorservice" binding="ws2007HttpBinding"
19: contract="Artech.NewFeaturesInWcf4.Contract.ICalculator" behaviorConfiguration="defaultBehavior" />
20: </service>
21: </services>
22: </system.serviceModel>
23: </configuration>
如果我们需要将这两个行为定义成默认的服务行为和终结点行为,我们只需像下面给出的配置一样将它们的name属性去掉。在这种情况下,对于该配置作用范围内配置的所有服务和终结点,如果并没有对其behaviorConfiguration进行显式设置,它们将具有对应的默认行为。
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <endpointBehaviors>
6: <behavior>
7: <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
8: </behavior>
9: </endpointBehaviors>
10: <serviceBehaviors>
11: <behavior>
12: <serviceDebug includeExceptionDetailInFaults="true"/>
13: </behavior>
14: </serviceBehaviors>
15: </behaviors>
16: ...
17: </system.serviceModel>
18: </configuration>
默认行为配置具有一个默认绑定配置所不具有的属性,那就是配置的继承性。我们举个简单的例子来说明默认行为配置的继承性所致为何。假设我创建一个如下图所示结构的用于服务寄宿(IIS寄宿)的Web项目。为了对.svc文件进行结构化的管理,我们对其进行分类,并将同类的.svc文件至于相同的子目录下。在本例中,我们建立了一个Erp的子目录用于存放所有关于ERP相关服务的.svc文件,在这里仅仅具有一个唯一的基于订单服务的OrderService.svc。
读者应该注意到了,在Web项目的根据和子目录Erp中均定义了一个Web.config。这也是很常用并且我个人推荐的配置方式:将公共的配置定义在外层的Web.config中,而子目录下Web.config用于定义在该目录下所有.svc文件对应的服务的配置。现在假设两个Web.config分别具有如下的配置。
WcfServices\Web.config:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <serviceBehaviors>
6: <behavior>
7: <dataContractSerializer ignoreExtensionDataObject="true" maxItemsInObjectGraph="65536" />
8: <serviceDebug includeExceptionDetailInFaults="true" />
9: </behavior>
10: </serviceBehaviors>
11: </behaviors>
12: </system.serviceModel>
13: </configuration>
WcfServices\Erp\Web.config:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <serviceBehaviors>
6: <behavior>
7: <dataContractSerializer maxItemsInObjectGraph="2147483647" />
8: <serviceTimeouts transactionTimeout="00:01:00" />
9: </behavior>
10: </serviceBehaviors>
11: </behaviors>
12: <services>
13: <service name="Artech.WcfServices.Service.OrderService">
14: <endpoint binding="ws2007HttpBinding" contract="Artech.WcfServices.Contract.IOrderService" />
15: </service>
16: </services>
17: </system.serviceModel>
18: </configuration>
从上面给出的两段配置我们可以看到,两个配置文件中均定义了默认的服务行为。那么对于基于OrderService.svc的服务OrderService来说,它具有怎样的服务行为呢?实际上,定义在根目录下Web.config的默认服务行为会自动被子目录\Erp所继承,所以OrderService具有的服务行为是两者的“合并”,它具有的默认服务行为和下面的配置等效。我们将定义在上级目录下默认行为配置被下级目录继承的特性称为默认行为配置的继承性。同理,所有没有显式设置具名行为的服务和终结点均会继承定义在machine.config中的默认行为。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <serviceBehaviors>
6: <behavior>
7: <dataContractSerializer maxItemsInObjectGraph="2147483647" />
8: <serviceTimeouts transactionTimeout="00:01:00" />
9: <serviceDebug includeExceptionDetailInFaults="true" />
10: </behavior>
11: </serviceBehaviors>
12: </behaviors>
13: ...
14: </system.serviceModel>
15: </configuration>