Windows 通信基础-2
这个服务的定义分成两个部分,一个是描述服务契约的接口,另一个是契约的实现。所有 WCF 服务都以这种方法定义。这个接口名为 IGreetingService,暴露一个函数,名为 Greet。正确的 WCF 契约还需要用System.ServiceModel.ServiceContractAttribute 标记接口,它应该包含服务的命名空间,在服务接口内部,将被暴露的每一个函数,都要用 OperationContractAttribute 去标记。每一个参数都有名字,这一点很重要。在 F# 中,创建接口的参数可以没有名字,简单地通过它们的类型定义就行。作为 WCF 契约的接口,如果其函数没有参数,是能够编译的,但是,当你调用这个服务时会报错,因为在 WCF 框架中,这个参数名是用来 (通过反射 reflection )创建数据,跨线发送的。GreetingService 类实现了契约,简单地提供一个问候语,会追加到“hello:”后面,无论传递的名字是什么。
为了把服务集成到网站服务器,需要创建一个 .svc 文件,它承担的角色与网站服务 的 .asmx 文件相似,告诉网站服务器什么类型应该用来处理这个服务请求。与这个服务一致的 .svc 示例就像下面显示的一样,整个文件通常只有一行。.svc文件中最重要的属性是 Service 属性,告诉网站服务器应该使用哪一种类型:
<% @ServiceHost Debug="true" Service="Strangelights.Services.GreetingService" %>
最后,必须配置服务。因为 WCF 提供了协议的选择,使用配置文件告诉使用哪一个。清单 10-9 的配置文件显示了能够用来配置服务的配置文件。Service 元素定义两个端点,这是客户端能够与服务对话的协议。其中一个端点是标准的网站服务 HTTP 绑定,另一个是元数据交换绑定;它允许服务暴露有关它自身的元数据,将告诉任何潜在的客户端,应该如何与服务对话。当你创建客户端代理时,将使用这个端点。
Listing 10-9. WCF 服务的配置文件
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.serviceModel>
<services>
<service
name="Strangelights.Services.GreetingService"
behaviorConfiguration="MyServiceTypeBehaviors">
<endpoint
contract="Strangelights.Services.IGreetingService"
binding="wsHttpBinding"/>
<endpoint
contract="Strangelights.Services.IGreetingService"
binding="mexHttpBinding" address="mex"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors" >
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
去创建这个服务的一个客户端,需要使用工具 SvcUtil.exe,它的目的与我们在 “创建网站服务”一节讨论过的wsdl.exe 工具相似。SvcUtil.exe 创建服务的代理,使用下面的命令行,适当地改变一下 URL:
svcutil.exe http://localhost:1033/WCFService/Service.svc?wsdl
这将生成一个 C# 代理文件,然后可以编译成一个 .NET 程序集,在 F# 中使用。还生成一个 .config 文件,可以用于配置任何客户端的应用程序。
一旦添加了对这个代理的 .dll 文件的引用,使用这个代理就变得非常简单了。只要创建这个代理的实例,用适当的参数调用它的 Greet 方法。清单 10-10 展示了代理的一个示例;因为调用代理的 Dispose 方法很重要,我在创建时把它包在了 using 函数中。
Listing 10-10. 调用 WCF 服务
using (new GreetingServiceClient())
(fun client ->
print_endline (client.Greet("Rob"))
read_line() |> ignore)
清单 10-11 是一个产生的配置文件的示例,已经删除了一些特定的内容,以保证示例运行得更加顺利。安全设置已经删除,因为这可能会引起示例失败,如果这是运行在一台脱离域控制器的计算机上(程序员在开发中很常见的一种情况!)。另外,产生的两个端点,有一个已经删除,因此,在代码中就不需要指定端点了。
Listing 10-11. 调用 WCF 服务的配置文件
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IGreetingService"
closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false"
hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288"
maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32"
maxStringContentLength="8192"
maxArrayLength="16384"
maxBytesPerRead="4096"
maxNameTableCharCount="16384" />
<reliableSession ordered="true"
inactivityTimeout="00:10:00"
enabled="false" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8080/service"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IGreetingService"
contract="IGreetingService"
name="WSHttpBinding_IGreetingService">
</endpoint>
</client>
</system.serviceModel>
</configuration>
清单 10-10 的运行结果如下:
Hello: Rob
警告 虽然删除安全设置对于保证示例运行顺利是件很好的事情,但是,在整个开发的生命周期中,安全是应该仔细考虑的一个重要方面。如果涉及正式的 WCF 开发,我强烈建议你尽可能为应用程序考虑适当的安全设置(例如,打算为用户提供哪种认证?)。有关 WCF 安全的进一步信息,请见http://strangelights.com/FSharp/Foundations/default.aspx/FSharpFoundations.WCFSecurity。