WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现]2012-12-06 cnblogs 蒋金楠(提供模拟程序)基于HTTP-GET的元数据发布方式与基于WS-MEX原理类似,但是ServiceMetadataBehavior需要做的更多额外的工作。原因很简单,由于在WS-MEX模式下,我们为寄宿的服务添加了相应的MEX终结点,那么当服务被成功寄宿后,WCF已经为元数据的消息交换建立了如图1所示的分发体系,我们需要做的仅仅是对MEX终结点的DispatchRuntime进行相应的定制而已。

图1 WCF服务端分发体系但是如果采用HTTP-GET模式,实际上我们需要从ChannelDispatcher开始,重新构建整个分发体系。接下来,我们在《WS-MEX原理》提供实例的基础上,对我们自定义ServiceMetadataBehaviorAttribute进行进一步的完善,使之同时对两种模式的元数据发布提供支持。 (Source Code从这里下载)首先,我们需要定义一个新的服务契约接口:IHttpGetMetadata,Get操作处理任何形式的消息请求,因为它的输入参数和返回类型均为Message,并且Action和ReplyAction为*。
1: using System.ServiceModel;
2: using System.ServiceModel.Channels;
3: namespace ServiceMetadataBehaviorSimulator
4: {
5: [ServiceContract(Name = "IHttpGetMetadata", Namespace = "http://www.artech.com/")]
6: public interface IHttpGetMetadata
7: {
8: [OperationContract(Action = "*", ReplyAction = "*")]
9: Message Get(Message msg);
10: }
11: }
然后我们让前面定义的MetadataProvisionService实现IHttpGetMetadata接口,在这里无需再写任何多余的代码,因为MetadataProvisionService已经具有了一个Get方法。
1: public class MetadataProvisionService : IMetadataProvisionService, IHttpGetMetadata
2: {
3: //省略成员
4: }
接下来的工作就是构建一个全新的ChannelDispatcher,以及关联EndpointDispatcher,最后对EndpointDispatcher的DispatchRuntime进行定制。为此,我单独写了一个方法:CreateHttpGetChannelDispatcher。
1: [AttributeUsage(AttributeTargets.Class)]
2: public class ServiceMetadataBehaviorAttribute : Attribute, IServiceBehavior
3: {
4: //其他成员
5: private const string SingletonInstanceContextProviderType = "System.ServiceModel.Dispatcher.SingletonInstanceContextProvider,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
6:private const string SyncMethodInvokerType = "System.ServiceModel.Dispatcher.SyncMethodInvoker,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
7:private const string MessageOperationFormatterType = "System.ServiceModel.Dispatcher.MessageOperationFormatter, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
8:
9: private static void CreateHttpGetChannelDispatcher(ServiceHostBase host, Uri listenUri, MetadataSet metadata)
10: {
11: //创建Binding
12: TextMessageEncodingBindingElement messageEncodingElement = new TextMessageEncodingBindingElement() { MessageVersion = MessageVersion.None };
13: HttpTransportBindingElement transportElement = new HttpTransportBindingElement();
14: Utility.SetPropertyValue(transportElement, "Method", "GET");
15: Binding binding = new CustomBinding(messageEncodingElement, transportElement);
16:
17: //创建ChannelListener
18: IChannelListener listener = binding.BuildChannelListener<IReplyChannel>(listenUri, string.Empty, ListenUriMode.Explicit, new BindingParameterCollection());
19: ChannelDispatcher dispatcher = new ChannelDispatcher(listener, "ServiceMetadataBehaviorHttpGetBinding", binding) { MessageVersion = binding.MessageVersion };
20:
21: //创建EndpointDispatcher
22: EndpointDispatcher endpoint = new EndpointDispatcher(new EndpointAddress(listenUri), "IHttpGetMetadata", "http://www.artech.com/");
23:
24: //创建DispatchOperation,并设置DispatchMessageFormatter和OperationInvoker
25: DispatchOperation operation = new DispatchOperation(endpoint.DispatchRuntime, "Get", "*", "*");
26: operation.Formatter = Utility.CreateInstance<IDispatchMessageFormatter>(MessageOperationFormatterType, Type.EmptyTypes, new object[0]);
27: MethodInfo method = typeof(IHttpGetMetadata).GetMethod("Get");
28: operation.Invoker = Utility.CreateInstance<IOperationInvoker>(SyncMethodInvokerType, new Type[] { typeof(MethodInfo) }, new object[] { method });
29: endpoint.DispatchRuntime.Operations.Add(operation);
30:
31: //设置SingletonInstanceContext和InstanceContextProvider
32: MetadataProvisionService serviceInstance = new MetadataProvisionService(metadata);
33: endpoint.DispatchRuntime.SingletonInstanceContext = new InstanceContext(host, serviceInstance);
34: endpoint.DispatchRuntime.InstanceContextProvider = Utility.CreateInstance<IInstanceContextProvider>(SingletonInstanceContextProviderType, new Type[] { typeof(DispatchRuntime) }, new object[] { endpoint.DispatchRuntime });
35: dispatcher.Endpoints.Add(endpoint);
36:
37: //设置ContractFilter和AddressFilter
38: endpoint.ContractFilter = new MatchAllMessageFilter();
39: endpoint.AddressFilter = new MatchAllMessageFilter();
40:
41: host.ChannelDispatchers.Add(dispatcher);
42: }
43: }