WCF绑定及不同消息版本的SOAP样式
绑定提供了必要的元素来为客户端和服务端构建通讯信道。绑定描述了传输协议、消息编码格式以及其它用于通信信道的通信协议。通过绑定,WCF封装了各种不同通信场景下所使用的配置。每一个绑定配置都必须提供一个传输协议、一个消息编码格式和一个相关消息版本信息。
传输协议:可以是http、https、tcp、命名管道或msmq等。
消息编码:描述消息如何格式化。可以是binary、text、mtom。
消息版本:可以是soap1.1、soap1.2。消息版本还可以设置是否支持ws-addressing协议。在与非.net平台进行互操作时,应该注意消息版本的选择。
每个绑定都有若干个绑定元素组成,可以用以下代码查看默认绑定元素以及绑定所用的消息版本。
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace WcfBinding
{
class Program
{
static void Main(string[] args)
{
OutPutBindingElements(new BasicHttpBinding());
OutPutBindingElements(new WSHttpBinding());
OutPutBindingElements(new NetTcpBinding());
OutPutBindingElements(new NetNamedPipeBinding());
OutPutBindingElements(new NetMsmqBinding());
Console.ReadLine();
}
static void OutPutBindingElements(Binding binding)
{
Console.WriteLine("Binding:{0} ", binding.GetType().Name);
Console.WriteLine("MessageVersion:{0} ", binding.MessageVersion);
BindingElementCollection elements = binding.CreateBindingElements();
foreach (BindingElement element in elements)
{
Console.WriteLine(" {0} ", element.GetType().FullName);
}
Console.WriteLine();
}
}
}
执行的结果如下:
从结果可以看出,只有BasicHttpBinding使用SOAP1.1而且是AddressingNone。其他绑定使用的都是SOAP1.2,Addressing10的消息版本。下面就写一些代码,查看下这两种消息格式格式化后的消息。为了方便的更换消息版本,使用CustomBinding类定义自定义绑定:
实体类:
using System;
using System.Runtime.Serialization;
namespace FruitModel
{
[DataContract(Namespace = "http://www.cnblogs.com/qiuwuyu")]
public class Fruit
{
private string m_Name = string.Empty;
private string m_Price = string.Empty;
public Fruit(string name, string price)
{
m_Name = name;
m_Price = price;
}
[DataMember]
public string Name
{
get{ return m_Name; }
set{ m_Name = value; }
}
[DataMember]
public string Price
{
get{return m_Price;}
set{ m_Price = value;}
}
}
[DataContract(Namespace = "http://www.cnblogs.com/qiuwuyu")]
public class FruitHeader
{
private string m_HeaderKey = string.Empty;
[DataMember]
public string HeaderKey
{
get{ return m_HeaderKey;}
set{ m_HeaderKey = value;}
}
public FruitHeader(string key)
{
m_HeaderKey = key;
}
}
}
服务契约:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using FruitModel;
namespace IFruitService
{
[ServiceContract(Namespace = "http://www.cnblogs.com/qiuwuyu")]
public interface IFruitPriceService
{
[OperationContract(Action="*",ReplyAction="*")]
Message GetFruit(Message m);
}
}
服务实现:
using System;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using IFruitService;
using FruitModel;
namespace FruitPriceService
{
public class FruitPriceService : IFruitPriceService
{
public Message GetFruit(Message m)
{
Console.WriteLine(m.ToString());
Message message=Message.CreateMessage(m.Version, "*", new Fruit("banana", "6.00"));
MessageHeader mHeader = MessageHeader.CreateHeader("FruitResponseHeader",
"http://www.cnblogs.com/qiuwuyu", new FruitHeader("Response"));
message.Headers.Add(mHeader);
return message;
}
}
}
寄存服务:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using IFruitService;
namespace WcfMessageHost
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(FruitPriceService.FruitPriceService),
new Uri("http://localhost:8000/")))
{
TextMessageEncodingBindingElement textElement = new TextMessageEncodingBindingElement(
MessageVersion.Soap12WSAddressing10, Encoding.UTF8);
CustomBinding customBind = new CustomBinding();
customBind.Elements.Add(textElement);
customBind.Elements.Add(new HttpTransportBindingElement());
host.AddServiceEndpoint(typeof(IFruitPriceService), customBind, "FruitService");
host.Open();
Console.WriteLine("Fruit Service Is Running...");
Console.ReadLine();
}
}
}
}
客户端调用:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.Text;
using IFruitService;
using FruitModel;
namespace WcfMessageClient
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Fruit Client Is Running...");
EndpointAddress epAddr = new EndpointAddress("http://localhost:8000/FruitService");
TextMessageEncodingBindingElement textElement = new TextMessageEncodingBindingElement(
MessageVersion.Soap12WSAddressing10, Encoding.UTF8);
CustomBinding customBind = new CustomBinding();
customBind.Elements.Add(textElement);
customBind.Elements.Add(new HttpTransportBindingElement());
IFruitPriceService proxy = ChannelFactory<IFruitPriceService>.CreateChannel(customBind, epAddr);
MessageHeader mHeader = MessageHeader.CreateHeader("FruitHeader", "http://www.cnblogs.com/qiuwuyu",
new FruitHeader("password"));
Message message = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "*",
new Fruit("apple","4.00"));
message.Headers.Add(mHeader);
Message m = proxy.GetFruit(message);
Console.WriteLine(m.ToString());
Console.ReadLine();
}
}
}
下面是MessageVersion.Soap11的请求消息:
MessageVersion.Soap11的返回消息:
把程序中相应的MessageVersion都更改MessageVersion.Soap12WSAddressing10后的请求消息:
MessageVersion.Soap12WSAddressing10后的返回消息:
从结果对比可以看出,两个版本的消息差别还是很大的。可以用BasicHttpBinding取代早期的asp.net web services服务而不会产生兼容问题,因为它采用的soap1.1以及早期安全标准。