WCF之消息

在WCF中客户端与服务端都是靠消息进行传输的,消息格式和SOAP一样,具有消息标头和正文。客户端和服务端之间的所有通讯最终都会产生System.ServiceModel.Channels. Message

实例。在平时操作中,很少直接操作Message类型,而是通过WCF服务模型构造(如数据契约、消息契约和操作契约)来描述传入消息和传出消息。所以,要想透彻的理解WCF有必要对Message多些了解。下面看一个简单的类,来了解一下Message用到的类。

using System;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.IO;
using System.Xml;
using System.Text;
using System.ServiceModel.Channels;

namespace WcfMessage
{
[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;
}
}
class Program
{
static void Main(string[] args)
{
Fruit fruit
= new Fruit("banana", "6.00");
//把Fruit类型序列化为xml文件
using (FileStream fs = new FileStream("Fruit.xml", FileMode.Create))
{
DataContractSerializer dataSer
= new DataContractSerializer(typeof(Fruit));
dataSer.WriteObject(fs, fruit);
}

XmlDocument xmlDoc
= new XmlDocument();
xmlDoc.Load(
"Fruit.xml");
//用二进制编码的xml文档
using (FileStream binStream = new FileStream("Fruit.bin", FileMode.Create))
{
using (XmlWriter xw = XmlDictionaryWriter.CreateBinaryWriter(binStream))
{
xmlDoc.WriteContentTo(xw);
}
}
//用mtom编码的xml文档
using (FileStream mtomStream = new FileStream("Fruit.mtom", FileMode.Create))
{
using (XmlWriter xw = XmlDictionaryWriter.CreateMtomWriter(mtomStream, Encoding.UTF8, 1024, "text/xml"))
{
xmlDoc.WriteTo(xw);
}
}

//创建一消息,并用文本编码的消息
Message message1 = Message.CreateMessage(MessageVersion.Soap11WSAddressingAugust2004, "*", fruit);
MessageHeader mHeader
= MessageHeader.CreateHeader("FruitHeader", "http://www.cnblogs.com/qiuwuyu",
 
new FruitHeader("password"));
message1.Headers.Add(mHeader);
using (FileStream stream = new FileStream("FruitMessageHeader.xml", FileMode.Create))
{
using (XmlWriter xw = XmlDictionaryWriter.CreateTextWriter(stream))
{
message1.WriteMessage(xw);
}
}
//创建一个没有消息版本,并用文本编码的消息
Message message = Message.CreateMessage(MessageVersion.None, "*", new XmlNodeReader(xmlDoc));
using (FileStream stream = new FileStream("FruitMessageNone.xml", FileMode.Create))
{
using (XmlWriter xw = XmlDictionaryWriter.CreateTextWriter(stream))
{
message.WriteMessage(xw);
}
}
}
}
}

XmlDictionary是定义了一个私有的可以表示SOAP消息中元素名字、属性和XML namespace声明的key-value列表的字典结构。字典在常见文本字符串和整数之间建立映射,并为压缩和解压缩XML提供一种有效机制。XmlDictionaryWriter是WCF中一个优化的读取XML编写器。

执行上述代码后,查看生成文件。

Fruit.xml文件:

二进制编码的Fruit.xml文件:

MTOM编码的Fruit.xml文件:

文本编码的消息:

无消息版本信息本文本编码的消息:

下面看一个直接用Message写的小实例,从整体上感觉下。

服务契约:

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);
}
}
Action指定为“*”表示可以处理服务端接收到的任何消息。如果没有指定Action值,则默认为服务约定命名空间/约定名称(默认为接口名称)/操作名称(默认为方法名称)。
ReplyAction为“*”表示不向消息中添加答复操作。如果没有指定Action值,则默认为服务约定命名空间/约定名称(默认为接口名称)/操作名称(默认为方法名称)Response。
在客户端应用程序中指定星号可指示 不验证答复操作。

服务实现:

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)
{
if ( OperationContext.Current.IncomingMessageHeaders.GetHeader<FruitHeader>(0).HeaderKey!="password" )
{
FaultCode faultCode
= new FaultCode("Invalid Key");
return Message.CreateMessage(m.Version, faultCode, "Invalid Key", "*");
}
return Message.CreateMessage(m.Version, "*", new Fruit("banana", "6.00"));
}
}
}

寄存服务:

using System;
using System.ServiceModel;
using IFruitService;

namespace WcfMessageHost
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(FruitPriceService.FruitPriceService),
new Uri("http://localhost:8000/Fruit")))
{
host.AddServiceEndpoint(
typeof(IFruitPriceService), new BasicHttpBinding(), "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 IFruitService;
using FruitModel;

namespace WcfMessageClient
{
class Program
{
static void Main(string[] args)
{
EndpointAddress epAddr
= new EndpointAddress("http://localhost:8000/Fruit/FruitService");
IFruitPriceService proxy
= ChannelFactory<IFruitPriceService>.CreateChannel(new BasicHttpBinding(), epAddr);

MessageHeader mHeader
= MessageHeader.CreateHeader("FruitHeader", " http://www.cnblogs.com/qiuwuyu",
 
new FruitHeader("password"));
Message message
= Message.CreateMessage(MessageVersion.Soap11, "*",string.Empty);
message.Headers.Add(mHeader);
Message m
= proxy.GetFruit(message);
if (m.IsFault)
{
Console.WriteLine(m.ToString());
}
else
{
Fruit fruit
= m.GetBody<Fruit>();
Console.WriteLine(
"Name:" + fruit.Name + " Price:" + fruit.Price);
}
Console.WriteLine(
"Fruit Client Is Running...");
Console.ReadLine();
}
}
}
传入正确的HeaderKey的执行结果:

传入错误的HeaderKey的执行结果:

如果HeaderKey不正确可以返回一个FaultMessage,如下 FaultCode faultCode = new FaultCode("InvalidKey"); return Message.CreateMessage(m.Version,faultCode, "Invalid Key", "*"); 在客户端可以用IsFault判断是否是FaultMessage并进行一些相应的逻辑处理。

posted @ 2011-04-14 08:32  秋无语  阅读(826)  评论(0编辑  收藏  举报