SOAP

SOAP是Simple Object Access Protocol(简单对象访问协议)的简称 而如今SOAP已经成为了符合W3C制定的SOAP规范的消息 允许您使用 XML 在通过低层 Internet 协议连接的系统之间进行通信 它为通过网络消息传输的 XML 信息提供了标准的封套  目前为止W3C一共制定了SOAP1.1和SOAP1.2两个版本的消息规范 对应的命名空间如下

SOAP1.1:http://schemas.xmlsoap.org/soap/envelope

SOAP1.2:http://www.w3.org/2003/05/soap-envelope

下面的XML体现了一个典型的SOAP消息的结构 整个消息被封装在称为SOAP封套的<Envelope>元素中 该元素包含了<Body>表示消息主体 <Header>表示消息报头 可以有多个消息报头

 <env:Envelope xmlns:env="http://www.w3.org/2003/0S/soap-envelope">
<env:Header>
<n:alertcontrol xmlns:n="http://example.org/alertcontrol">
<n:priority>l</n:priority>
<n:expires>--22T14::-0S:</n:expires>
</n:alertconcrol>
</env:Header>
<env:Body>
<m:alert xmlns:m="http://example.org/alert">
    <m:msg>Pick up Mary at school at 2pm</m:msg>
</m:alert>
</env:Body>
</env:Envelope>

WS-Addressing

SOAP规范了XML消息的结构 而WS-Addressing则用于规范消息交换中的寻址机制 目前W3C发布了WS-Addressing的两个版本 WS-Addressing2004和WS-Addressing1.0 对应的命名空间如下

WS-Addressing2004:http://schemas.xmlsoap.org/2004/08/addressing

WS-Addressing1.0:http://www.w3.org/2005/08/addressing

下面的XML体现了寻址机制在SOAP消息结构中的应用 标红的几个元素是WS-Addressing规范的消息寻址机制中典型的消息寻址报头元素 它们都放在Header报头中 一个报头可以有多个报头元素

 <S:Envelope xmlns:S="http://www.w3.org/2003/0S/soap-envelope" xmlns:wsa="http://www.w3.org/200S/0S/addressing">
<S:Header>
<wsa:MessageID> http://~xample.com/6B29FC40-CA47-1067-B31D-OODDOI0662DA </wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address> http://example.com/business/clientl </wsa:Address> .
</wsa:ReplyTo>
<wsa:To> http://example.com/fabrikam/Purchasing </wsa:To>
<wsa:Action> http://example.com/fabrikam/SubmitPO </wsa:Accion>
</S:Header>
<S:Body> ... </S:Body>
</S:Envelope>

WS-Addressing中规范的报头元素有如下几种

可选的报头元素

<To>:以URI的形式表示消息发送到的目标终结点 默认值:http://www.w3.org/2005/08/addressing/anonymous

<Form>:表示发送消息的终结点地址 不常用 因为消息发送的终结点地址不需要手动指定

<ReplyTo>:表示接收回复消息的终结点地址 默认值与<To>元素值等同

<FaultTo>:表示接收回复的错误消息的终结点地址

<MessageID>:以URI的形式表示消息的唯一标识

<RelatesTo>:与某个具有唯一标识的消息关联起来 要关联 只需设<RelatesTo>的值=<MessageID>的值

必须的报头元素

<Action>:以URI的形式来表示消息的意图 比如以URI的形式表示要调用的服务操作 如:http://www.cnblogs.com/Test

消息版本

WCF中用System.ServiceModel.Channels.MessageVersion类表示消息版本 它作为表示消息的System.ServiceModel.Channels.Message的Version属性提供消息版本 消息的版本是由消息使用的SOAP的版本和WS-Addressing的版本共同决定的

MessageVersion具有如下公开属性

Envelope

返回一个EnvelopeVersion对象 表示消息使用的SOAP的版本 Envelope通过以下属性表示SOAP版本

Envelope.None:非SOAP消息

Envelope.Soap11:SOAP1.1版本

Envelope.Soap12:SOAP1.2版本

Addressing

返回一个AddressingVersion对象 表示消息使用的WS-Addressing的版本 Addressing通过以下属性表示WS-Addressing的版本

Addressing.None:消息不支持WS-Addressing

Addressing.WSAddressingAugust2004:WS-Addressing2004版本

Addressing.WSAddressing10:WS-Addressing1.0版本

MessageVersion具有如下静态属性

Soap11

获取使用 SOAP 1.1 的消息版本

Soap12

获取使用 SOAP 1.2 的消息版本

Default

获取WCF所用的默认消息版本 默认消息版本为SOAP1.2和WS-Addressing1.0组合的消息版本

None

获取不使用 SOAP 和 WS-Addressing 的消息版本

Soap11WSAddressingAugust2004

获取SOAP1.1和WS-Addressing2004组合的消息版本

Soap11WSAddressing10

获取SOAP1.1和WS-Addressing1.0组合的消息版本

Soap12WSAddressingAugust2004

获取SOAP1.2和WS-Addressing2004组合的消息版本

Soap12WSAddressing10

获取SOAP1.2和WS-Addressing1.0组合的消息版本

MessageVersion具有如下静态方法

CreateVersion(EnvelopeVersion envelopeVersion)

创建消息的版本 参数指定SOAP版本 默认组合WS-Addressing1.0版本

CreateVersion(EnvelopeVersion envelopeVersion, AddressingVersion addressingVersion)

创建消息的版本 参数指定SOAP版本和WS-Addressing版本

创建消息

Message类的静态方法CreateMessage用于创建消息 有如下重载 (? 测过需要使用管理员身份运行 不知道为什么 )创建后调用实例方法WriteMessage将消息序列化为xml WriteMessage会自动调用DataContractSerializer来序列化消息 需要添加对System.Runtime.Serialization的引用

Message.CreateMessage(MessageVersion version , string action)

例子1

 using System.ServiceModel.Channels;
using System.Diagnostics; string action = "http://www.artech.com/ICaculator/Add";
Message message = Message.CreateMessage(MessageVersion.Default, action);
XmlWriter writer = new XmlTextWriter("Test.xml", Encoding.UTF8);
message.WriteMessage(writer);
writer.Close();
message.Close();
Process.Start("Test.xml");

Message.CreateMessage(MessageVersion version , string action , object body)

参数body是一个可被序列化的对象 如

例子2

 [DataContract(Namespace = "http://www.cnblogs.com/")]
public class Order
{
[DataMember(Order = )]
public Guid ID { get; set; }
[DataMember(Order = )]
public DateTime Date { get; set; }
[DataMember(Order = )]
public string Customer { get; set; }
[DataMember(Order = )]
public string Address { get; set; }
} Message message = Message.CreateMessage(MessageVersion.None, action, order);
……

Message.CreateMessage(MessageVersion version , string action , BodyWriter bodywriter)

参数bodywriter是一个派生于抽象类BodyWriter的类型 可重写它的OnWiteBodyContents创建消息 我们使用前一个例子的Test.xml文件 在自定义的myBodyWriter中将其数据读取出来用以创建主体 接着使用Message的WriteMessage方法将创建的消息写入Test2.xml

例子3

 public class myBodyWriter : BodyWriter
{
private string fileName { get; set; }
public myBodyWriter(string fileName):base(false)
{
this.fileName = fileName;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
XmlReader reader = new XmlTextReader(fileName);
while (!reader.EOF)
{
writer.WriteNode(reader, false);
}
}
} string action = "http://www.artech.com/ICaculator/Add";
Message message = Message.CreateMessage(MessageVersion.Default, action, new myBodyWriter("Test.xml"));
XmlWriter writer = new XmlTextWriter("Test2.xml", Encoding.UTF8);
message.WriteMessage(writer);
writer.Close();
message.Close();
Process.Start("Test2.xml");

Message.CreateMessage(MessageVersion version , string action , XmlReader xmlreader)

参数xmlreader用于读取一个xml文件 将其作为CreateMessage的参数 可以将读到的xml序列化为指定版本的SOAP消息 还是使用例子2的Test.xml文件来描述此重载的用法 如

例子4

 string action = "http://www.artech.com/ICaculator/Add";
Message message = Message.CreateMessage(MessageVersion.Soap11WSAddressing10, action, new XmlTextReader("Test.xml"));
XmlWriter writer = new XmlTextWriter("Test3.xml", Encoding.UTF8);
message.WriteMessage(writer);
writer.Close();
message.Close();
Process.Start("Test3.xml");

Message.CreateMessage(MessageVersion version , MessageFault messagefault , string action)

对于服务来说 请求被正常的处理 则返回一个普通的SOAP消息 错误时则可以使用此重载返回错误消息

 using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Diagnostics; //自定义错误码
FaultCode faultcode=FaultCode.CreateSenderFaultCode("calculatorError","http://www.cnblogs.com");
//错误列表
List<FaultReasonText> list=new List<FaultReasonText>{
new FaultReasonText("试图除以0")
};
//将错误列表插入错误正文
FaultReason reason=new FaultReason(list);
//创建错误消息
MessageFault msgfault=MessageFault.CreateFault(faultcode,reason);
//创建消息
string action = "http://www.artech.com/ICaculator/Add";
Message message = Message.CreateMessage(MessageVersion.Soap11WSAddressing10, msgfault, action);
XmlWriter writer = new XmlTextWriter("error.xml", Encoding.UTF8);
message.WriteMessage(writer);
writer.Close();
message.Close();
Process.Start("error.xml");

读取消息

Message类的实例方法GetBody用于读取刚创建的还未被写入文件的消息的主体内容并将内容反序列化为对象 如果消息已被写入文件则无法读取 有如下方法

Message.GetBody<T>()

读取消息主体内容并反序列化为对象返回

 Order order = new Order
{
ID = Guid.NewGuid(),
Date = DateTime.Now,
Address = "xx",
Customer = "sam"
}; string action = "http://www.artech.com/ICaculator/Add";
Message message = Message.CreateMessage(MessageVersion.None, action,order);
Order ReadOrder = message.GetBody<Order>();
message.Close();
Console.WriteLine(ReadOrder.Customer + "," + ReadOrder.Address);
Console.Read();

Message.GetBody<T>(XmlObjectSerializer serializer)

功能同上 可以指定反序列化的序列化器 默认是DataContractSerializer

Message.GetReaderAtBodyContents()

此方法会返回一个XmlDictionaryReader对象 可以通过此对象进一步提取消息主体部分的内容

写入消息

Message类提供了一系列的WriteXx实例方法用于将消息写入  方法如下

WriteStartBody

WriteBody

WriteBodyContents

WriteStartEnvelope

WriteMessage

拷贝消息

当消息被创建后(未写入的时候) 可以使用Message读取或写入消息 一旦消息被读取后 则不能再次读或写 一旦消息被写入后 则不能再次写读或 即被创建的消息只能读一次 不能再次对消息有任何读写操作 写也是一样的 我们可以通过Message的CreateBufferedCopy实例方法创建消息缓存 并使用MessageBuffer的CreateMessage来重建消息对象 如此可以无限制的重复调用MessageBuffer的CreateMessage创建多个副本 用以重复读或者重复写

 string action = "http://www.artech.com/ICaculator/Add";
Message message = Message.CreateMessage(MessageVersion.None, action,order);
//获取消息缓存
MessageBuffer messagebuffer = message.CreateBufferedCopy(int.MaxValue);
//创建消息副本1
Message copymessage= messagebuffer.CreateMessage();
//读
Order ReadOrder = copymessage.GetBody<Order>();
Console.WriteLine(ReadOrder.Customer + "," + ReadOrder.Address);
//创建消息副本2
Message copymessage2 = messagebuffer.CreateMessage();
//第二次读
Order ReadOrder2 = copymessage2.GetBody<Order>();
Console.WriteLine(ReadOrder2.Customer + "," + ReadOrder2.Address);
message.Close();
messagebuffer.Close();

消息报头

MessageHeaderInfo类

使用MessageHeaderInfo表示消息报头 他是所有消息报头的抽象基类 具有以下属性

Name

报头的名称

NameSpace

和报头的命名空间

Actor

此属性指定了消息报头的目标接受方 对于SOAP的消息来说 消息主体部分一般都是针对消息的最终接收者的 但消息报头则可能是发送给中介者或者最终接收者 Actor属性就是用于指定某个报头的接收方的角色 SOAP规范使用如下URI表示处理报头的角色

http://schemas.xmlsoap.org/soap/actor/next

http://schemas.xmlsoap.org/soap/actor/ultimateReceiver

http://schemas.xmlsoap.org/soap/actor/none

如果你的消息使用的SOAP版本是1.1版本 则编程中使用以上URI来表示角色 如果是SOAP1.2则使用如下URI来表示角色

http://www.w3c.org/2003/05/soap-envelope/role/next

http://www.w3c.org/2003/05/soap-envelope/role/ultimateReceiver

http://www.w3c.org/2003/05/soap-envelope/role/none

next指定报头的接收方可以是中介者也可以是最终接收者

ultimateReceiver指定报头的接收方是最终接收者

none指定报头的接收方可以是任何角色

MustUnderstand

表示报头是否是强制报头

如果设为true 则认为是一个强制报头 那么如果该报头同时指定了报头接收方(Actor) 则接收方必须处理此报头 即如果你需要强制接收方处理此报头 则需要将此属性设为true 并显示指定Actor 否则不需要指定Actor和MustUnderstand

Relay

表示报头是否是一个中继报头 此属性主要针对消息中介 默认false

IsReferenceParameter

表示报头是否是某个终结点引用的参数

MessageHeader类

MessageHeader派生于MessageHeaderInfo 并重写了基类的4个属性 并为每个属性指定了默认值 Actor默认值为null 其余三个(MustUnderstand、Relay、IsReferenceParameter)默认值为false MessageHeader是抽象类 无法实例化 所以它提供了静态方法CreateHeader来创建报头 该方法具有8个重载版本 大体上所接收的参数如下

参数name指定报头名字、ns指定命名空间、value指定可被序列化的对象、mustUnderstand指定是否是强制报头、serializer指定序列化器 默认是DatacontractSerializer、actor指定处理消息报头的节点类型、relay指定是否是中继报头

MessageHeader具有如下几个方法

IsMessageVersionSupported

判断当前报头是否支持指定的消息属性

WriteHeaderContents/WriteStartHeader

实现对消息或消息报头的写入

MessageHeader<T>类

此类也表示消息报头 但他是一个强类型的消息报头且可以实例化 在需要创建自定义报头时可以使用它来创建强类型的对象来作为消息报头 T类型表示作为消息报头值的对象的类型 此类也具有Actor、MustUnderstand和Relay属性 Content属性用于获取或设置表示消息报头值的对象 此类不继承MessageHeader 所以不能直接将其添加到消息报头集合中 可以调用其GetUntypedHeader方法将自身转换为MessageHeader类型 然后再添加到报头集合 转换时需要定义报头的名字和命名空间 如

 MessageHeader<string> foo = new MessageHeader<string>("xxx");
message.Headers.Add(foo.GetUntypedHeader("xxxxx","xxxxx"));

MessageHeaders类

此类表示消息报头集合 通过表示消息的Message对象的Headers只读属性可以获取 MessageHeaders具有与前面介绍过的WS-Addressing的7个寻址报头元素一样的可读写的属性 它们是:

To

以URI的形式表示消息发送到的目标终结点

Form

设置或获取发送消息的终结点地址 不常用 因为消息发送的终结点地址不需要手动指定

ReplyTo

设置或获取接收回复消息的终结点地址

FaultTo

设置或获取接收回复的错误消息的终结点地址

MessageID

设置或获取消息的唯一ID

RelatesTo

设置或获取与消息相关的消息ID

Action

以URI的形式来表示消息的意图 比如以URI的形式表示要调用的服务操作 如:http://www.cnblogs.com/Test

这7个属性可用于设置或获取报头的信息 每个属性其实就是一个内置的报头 所以无需手动创建MessageHeader或MessageHeaderInfo 除非自定义报头 除开这7个属性外 此类还具有UnderstoodHeaders只读属性(用于获取所有MustUnderstand为true的报头集合 )、MessageVersion(获取消息版本)

MessageHeaders具有如下方法用于获取单个报头或将单个报头添加到报头集合:

Add(MessageHeader header)

将MessageHeader类型的报头添加到报头集合中

GetHeader<T>(string headerName,string headerNamespace)

根据指定的报头名称和报头的命名空间获取一个报头 T表示报头的类型

现在来演示一遍如何为消息添加寻址报头和自定义报头

 string ns = "http://www.artech.com/crm";
string action = "http://www.artech.com/crm/addCustomer";
EndpointAddress address = new EndpointAddress("http://www.artech.com/crm/client");
Message message = Message.CreateMessage(MessageVersion.Soap11WSAddressingAugust2004, action);
//指定6个寻址报头的值
message.Headers.To = new Uri("http://www.artech.com/crm/customerService");
message.Headers.From = address;
message.Headers.ReplyTo = address;
message.Headers.FaultTo = address;
Guid id = Guid.NewGuid();
message.Headers.MessageId = new UniqueId(id);
message.Headers.RelatesTo = new UniqueId(id);
//自定义的消息报头
MessageHeader<string> foo = new MessageHeader<string>("ABC");
//参数3为actor 它指定了自定义报头bar的接收方可以是中介者或者最终接收者 参数2为mustUnderstand 值为true 表示此报头必须被接收者处理
MessageHeader<string> bar = new MessageHeader<string>("abc", true, "http://schemas.xmlsoap.org/soap/actor/next",false);
MessageHeader<string> baz = new MessageHeader<string>("",false,"http://schemas.xmlsoap.org/soap/actor/next",false);
//转换自定义报头
message.Headers.Add(foo.GetUntypedHeader("foo",ns));
message.Headers.Add(foo.GetUntypedHeader("bar", ns));
message.Headers.Add(foo.GetUntypedHeader("baz", ns));
XmlWriter writer = new XmlTextWriter("1.xml", Encoding.UTF8);
message.WriteMessage(writer);
writer.Close();
message.Close();
Process.Start("1.xml");

结果如下

 <s:Envelope xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<a:Action s:mustUnderstand="">http://www.artech.com/crm/addCustomer</a:Action>
<a:To s:mustUnderstand="">http://www.artech.com/crm/customerService</a:To>
<a:From>
<a:Address>http://www.artech.com/crm/client</a:Address>
</a:From>
<a:ReplyTo>
<a:Address>http://www.artech.com/crm/client</a:Address>
</a:ReplyTo>
<a:FaultTo>
<a:Address>http://www.artech.com/crm/client</a:Address>
</a:FaultTo>
<a:MessageID>urn:uuid:c342e0c8-d056-4ba5-8f35-f8a399dd3fd9</a:MessageID>
<a:RelatesTo>urn:uuid:c342e0c8-d056-4ba5-8f35-f8a399dd3fd9</a:RelatesTo>
<foo xmlns="http://www.artech.com/crm">ABC</foo>
<bar xmlns="http://www.artech.com/crm">ABC</bar>
<baz xmlns="http://www.artech.com/crm">ABC</baz>
</s:Header>
<s:Body />
</s:Envelope>

消息属性

Properties

Properties是一个MessageProperties类型的对象 表示消息的消息属性集合 这些消息属性集合是附加到消息中的命名数据段 且通常不会在发送消息时发出 它仅仅在本地使用 如果需要 比如可以将消息属性附加到消息上 以供信道使用 它是一个键为string值为object的MessageProperties字典对象 实现了IDictionary<string,object>接口 它具有如下方法用于获取单个消息属性和添加单个消息属性到消息属性集合:

Add(string name,object property)

根据指定的消息属性名称和消息属性对象添加到消息集合中

MessageProperties[string propertyName]

获取单个消息属性只需要通过为数组指定某个消息属性的名称即可获取单个消息属性对象

Http消息属性

Http消息属性仅用于Http传输方式的消息 可以通过以下两个Http消息属性对象定制Http报头和Http查询字符串、cookie等 以下两个对象都有自己的报头属性集合 这里指的是Http报头 区别于消息的报头 消息的报头是封装在<Envelope>元素里的子元素<Header> 而Http报头不属于<Envelope>元素的子元素 它们是显示在消息的最开头位置的Http消息中的报头

HttpRequestMessageProperty

ns:System.ServiceModel.Channels.HttpRequestMessageProperty

如果传输方式为Http 则可以使用此对象 它也表示消息的属性 但仅用于Http传输方式的SOAP消息 可以使用此对象为SOAP消息添加Http报头和查询字符串 该对象具有如下属性

Name

静态属性 HttpRequestMessageProperty对象在消息的消息属性集合中的键名称 返回httpRequest

Headers

Http请求消息的报头集合

QueryString

查询字符串 默认返回空

SuppressEntityBody

是否忽略Http请求消息的主体 只发送报头部分

HttpResponseMessageProperty

ns:System.ServiceModel.Channels.HttpResponseMessageProperty

与HttpRequestMessageProperty有类似的行为 只不过此对象表示的是Http回复消息

Name

静态属性 HttpResponseMessageProperty对象在消息的消息属性集合中的键名称 返回httpResponse

Headers

Http回复消息的报头集合

SuppressEntityBody

是否忽略Http回复消息的主体 只发送报头部分

StatusCode

以System.Net.HttpStatisCode枚举的形式返回回复状态码

StatusDescriptiom

获取对状态的描述

SuppressPreamble

是否忽略消息序文

服务操作上下文

OperationContext类

服务操作上下文通过OperationContext表示 上下文对象可以获取当前执行的信道、当前线程的执行上下文、消息的消息属性集合和报头集合等等 它通过Current静态属性返回一个OperationContext实例对象 实例属性如下

IncomingMessageHeaders

获取入栈消息的报头集合 返回一个MessageHeaders对象 通过MessageHeaders的Add和GetHeader<T>可添加、获取单个报头

IncomingMessageProperties

获取入栈消息的消息属性集合 返回一个MessageProperties字典对象 通过MessageProperties的Add和[]可添加、获取单个消息属性

OutgoingMessageHeaders

获取出栈消息的报头集合 返回一个MessageHeaders对象

OutgoingMessageProperties

获取出栈消息的消息属性集合 返回一个MessageProperties字典对象

实例1:使用第一章的Calculator计算服务的例子 通过服务操作上下文对象为请求和回复消息添加http报头cookie

首先将Client项目的app.config的消息发送地址的端口改为8888

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://127.0.0.1:8888/calculatorservice" binding="basicHttpBinding" contract="CalculatorService" name="calculatorservice" />
</client>
</system.serviceModel>
</configuration>

再将Hosting项目的app.config的消息侦听地址改为7777

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="Service.CalculatorService">
<endpoint address="http://127.0.0.1:7777/calculatorservice" binding="basicHttpBinding" contract="CalculatorService"/>
</service>
</services>
</system.serviceModel>
</configuration>

在Service服务操作中为出栈消息添加Http报头

 using System.ServiceModel;
using Service.Interface;
using System.ServiceModel.Channels;
using System.Net;
using System.Diagnostics; namespace Service
{
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class CalculatorService:ICalculator
{
public double Add(double x, double y)
{ string responseMsgName = HttpResponseMessageProperty.Name;
//创建Http消息的消息属性并添加到消息属性集合中
HttpResponseMessageProperty messageProperty = new HttpResponseMessageProperty();
OperationContext.Current.OutgoingMessageProperties.Add(responseMsgName, messageProperty);
//为消息属性添加报头
messageProperty.Headers.Add(HttpResponseHeader.SetCookie, "Timestamp=" + Stopwatch.GetTimestamp());
return x + y;
}
}
}

在Client项目的请求消息中添加出栈消息的Http报头

 using System.ServiceModel;
using System.ServiceModel.Channels;
using Service.Interface;
using System.Net; namespace Client
{
class Program
{
static void Main(string[] args)
{
string securityToke = Guid.NewGuid().ToString();
string requestMsgName = HttpRequestMessageProperty.Name;
ChannelFactory<ICalculator> ChannelFactory = new ChannelFactory<ICalculator>("calculatorservice");
ICalculator proxy = ChannelFactory.CreateChannel();
OperationContextScope scope = new OperationContextScope(proxy as IContextChannel);
HttpRequestMessageProperty requestMsgPerty = new HttpRequestMessageProperty();
OperationContext.Current.OutgoingMessageProperties.Add(requestMsgName, requestMsgPerty);
requestMsgPerty.Headers.Add(HttpRequestHeader.Cookie, "SecurityToken=" + securityToke);
//调用服务
proxy.Add(, );
//释放资源
ChannelFactory.Close();
scope.Dispose();
Console.Read();
}
}
}

使用tcpTrace拦截消息 输出如下

注意配置终结点时 binding必须是basicHttpBinding 因为HttpResponseMessageProperty和HttpRequestMessageProperty仅仅适用于Http方式的请求和回复

实例2:使用第一章的Calculator计算服务的例子 客户端将回复消息的报头读取出来显示 服务端将请求消息的报头读取出来显示

要实现此操作 可以在契约接口中定义一个字典集合数据契约类 将字典集合数据契约类作为自定义报头的类型

 using System.Runtime.Remoting.Messaging;
using System.Runtime.Serialization; namespace Service.Interface
{
//指定序列化后 集合数据契约的元素名(ItemName)、键的元素名(KeyName)、值的元素名(ValueName)
[CollectionDataContract(Namespace = "http://www.cnblogs.com/", ItemName = "Message", KeyName = "Key", ValueName = "Value")]
public class ApplicationContext : Dictionary<string, string>
{
public const string callContextKey = "__applicationContext";
public static string HeaderLocalName = "ApplicationContext";
public static string HeaderNamespace = "http://www.cnblogs.com/";
public static ApplicationContext Current
{
get
{
if (CallContext.GetData(callContextKey) == null)
{
CallContext.SetData(callContextKey, new ApplicationContext());
}
return (ApplicationContext)CallContext.GetData(callContextKey);
}
} //参与序列化的成员
public string UserName
{
get { return (this.ContainsKey("UserName") == true ? this["UserName"] : string.Empty); }
set { this["UserName"] = value; }
} //参与序列化的成员
public string Department
{
get { return (this.ContainsKey("Department") == true ? this["Department"] : string.Empty); }
set { this["Department"] = value; }
}
}
}

Client项目发送请求消息 并附加自定义报头

 using System.ServiceModel;
using Service.Interface; using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
{
var proxy = channelFactory.CreateChannel();
using (OperationContextScope scope = new OperationContextScope(proxy as IContextChannel))
{
//创建出栈消息的自定义报头
ApplicationContext.Current.UserName = "Sam";
ApplicationContext.Current.Department = "IT";
//将自定义的字典集合数据契约作为自定义报头的类型
MessageHeader<ApplicationContext> OutHeader = new MessageHeader<ApplicationContext>(ApplicationContext.Current);
//将自定义报头添加到报头集合
OperationContext.Current.OutgoingMessageHeaders.Add(OutHeader.GetUntypedHeader(ApplicationContext.HeaderLocalName, ApplicationContext.HeaderNamespace));
Console.WriteLine("出栈消息的自定义报头值:{0}", ApplicationContext.Current.UserName);
Console.WriteLine("出栈消息的自定义报头值:{0}", ApplicationContext.Current.Department);
Console.WriteLine(proxy.Add(,));
//获取入栈消息的自定义报头
ApplicationContext InHeader = OperationContext.Current.IncomingMessageHeaders.GetHeader<ApplicationContext>(ApplicationContext.HeaderLocalName, ApplicationContext.HeaderNamespace);
Console.WriteLine("入栈消息的自定义报头值:{0}", InHeader.UserName);
Console.WriteLine("入栈消息的自定义报头值:{0}", InHeader.Department);
Console.Read();
}
}

Service项目回复消息 并附加自定义报头

 using System.ServiceModel;
using Service.Interface; [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class CalculatorService:ICalculator
{
public double Add(double x, double y)
{
//获取入栈消息的报头集合 取出ApplicationContext报头
ApplicationContext InHeader = OperationContext.Current.IncomingMessageHeaders.GetHeader<ApplicationContext>(ApplicationContext.HeaderLocalName, ApplicationContext.HeaderNamespace);
Console.WriteLine("入栈消息的自定义报头值:{0}", InHeader.UserName);
Console.WriteLine("入栈消息的自定义报头值:{0}", InHeader.Department);
//将服务端的ApplicationContext添加到回复消息的报头集合中
ApplicationContext.Current.UserName = "Leo";
ApplicationContext.Current.Department = "Admin";
MessageHeader<ApplicationContext> OutHeader = new MessageHeader<ApplicationContext>(ApplicationContext.Current);
OperationContext.Current.OutgoingMessageHeaders.Add(OutHeader.GetUntypedHeader(ApplicationContext.HeaderLocalName, ApplicationContext.HeaderNamespace));
Console.WriteLine("出栈消息的自定义报头值:{0}", ApplicationContext.Current.UserName);
Console.WriteLine("出栈消息的自定义报头值:{0}", ApplicationContext.Current.Department);
return x + y;
}
}

客户端和服务端分别输出如下消息

*创建消息报头使用MessageHeader<T> 创建完成后 将其转换为MessageHeader 然后使用OperationContext.Current.OutgoingMessageHeaders的Add方法将报头添加到报头集合中

*创建Http报头 则使用HttpRequestMessageProperty或HttpResponseMessageProperty的Headers.Add方法 添加完成后 使用OperationContext.Current.OutgoingMessageProperties的Add方法将HttpRequestMessageProperty或HttpResponseMessageProperty添加到消息属性集合中

消息契约

MessageContract特性

作为WCF四大契约(服务契约、数据契约、消息契约、错误契约)之一的消息契约 可以将接口或类声明为消息契约 可以指定接口或类的成员为消息报头或消息主体 使用MessageContract特性表示消息契约 它有如下属性

ProtectionLevel

设置消息契约的安全保护级别

IsWrapped

是否将表示消息主体的成员封装到一个父元素下 默认值true

WrapperName

指定要封装表示消息主体成员的父元素的名字 默认值是消息契约类型的名字

WrapperNameSpace

指定要封装表示消息主体成员的父元素的命名空间 默认值Http://tempuri.org

MessageHeader特性

使用该特性表明消息契约的消息成员将作为消息的消息报头 它有如下属性

ProtectionLevel

设置消息契约成员的安全保护级别

Name

消息契约成员的别名 此别名会体现在生成的xml消息中 如果没有别名 则默认为成员的名字

NameSpace

消息契约成员的命名空间

Actor

指定此报头的目标接受方 默认空

MustUnderstand

指定此报头是否是强制报头 默认false

Relay

指定此报头是否是中继报头 默认false

MessageBodyMember特性

使用该特性表明消息契约的消息成员将作为消息的消息主体 它只具有一个属性Order 用于指定此成员出现在消息主体中的位列

消息转换器

TypedMessageConverter类

ns:System.ServiceModel.Description

此类提供了方法用于消息契约和消息之间的转换  它具有如下几个方法

静态方法 Create(Type messageContractType , string action)

TypedMessageConverter类是一个抽象类 只能通过调用自身的静态方法Create来创建TypedMessageConverter对象 参数messageContractType 表示消息契约的类型 参数action表示要调用的服务操作的URI表示

FromMessage(Message message)

将消息转换为消息契约

ToMessage(object messageContractObject)

将参数指定的消息契约的实例对象转换为消息 返回一个Message对象 如

 using System.ServiceModel;
using System.ServiceModel.Description; [MessageContract]
public class Employee
{
[MessageHeader]
public string ID { get; set; }
[MessageBodyMember]
public string Name { get; set; }
[MessageBodyMember]
public string Gender { get; set; }
[MessageBodyMember]
public string Department { get; set; }
} Employee employee = new Employee
{
ID = "xxx0101",
Gender = "男",
Name = "sam",
Department = "IT"
}; string action = "http://www.cnblogs.com/employee/AddEmployee";
TypedMessageConverter typedMessageConverter=TypedMessageConverter.Create(typeof(Employee), action);
Message message=typedMessageConverter.ToMessage(employee);
XmlWriter writer = new XmlTextWriter("1.xml", Encoding.UTF8);
message.WriteMessage(writer);
writer.Close();
message.Close();
Process.Start("1.xml");

生成的xml结构如下 可以看到消息版本采用WCF默认的SOAP1.2和WS-Addressing1.0 ID成员作为报头 其它几个成员则作为了消息的主体

 <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="">http://www.cnblogs.com/employee/AddEmployee</a:Action>
<h:ID xmlns:h="http://tempuri.org/">xxx0101</h:ID>
</s:Header>
<s:Body>
<Employee xmlns="http://tempuri.org/">
<Department>IT</Department>
<Gender>男</Gender>
<Name>sam</Name>
</Employee>
</s:Body>
</s:Envelope>

*数据契约一般用于服务操作的输入和输出参数 消息契约同样可以作为操作的输入参数 但有限制 如果将消息契约作为服务操作的参数 则该操作只能有这一个参数 不能有额外参数 且 不能讲消息契约作为操作的输出参数

XML编码

消息在通过传输层发送之前必须经过编码 而通过传输层接收到的消息则需要解码才能被处理 编码使用XmlDictionaryWriter 解码使用XmlDictionaryReader

XmlDictionary类

ns:System.Xml

消息的编码对象和消息的解码对象必须对它们所处理的消息具有相同的理解才能正确编码解码消息 而它们对于消息的理解是建立在一个词汇表上的 即编码方按照词汇表对消息进行编码 解码方则按照词汇表对消息进行解码  词汇表使用XmlDictionary类来表示

XmlDictionaryString类

表示Xml键值的对象 而XmlDictionary就是一组XmlDictionaryString对象的集合 但XmlDictionaryString并不实现IEnumerable接口 不能对其进行遍历 可以直接创建XmlDictionaryString对象将其添加到XmlDictionary中 如

 XmlDictionary dictionary = new XmlDictionary();
XmlDictionaryString obj1 = new XmlDictionaryString(dictionary, "id", );
XmlDictionaryString obj2 = new XmlDictionaryString(dictionary, "employee", );
XmlDictionaryString obj3 = new XmlDictionaryString(dictionary, "address", );

或者通过XmlDictionary的Add方法 添加XmlDictionaryString对象

XmlDictionary dictionary = new XmlDictionary();
dictionary.Add("id"),
dictionary.Add("employee"),
dictionary.Add("address")

使用第二种方式 则添加的XmlDictionaryString对象的Key会按添加顺序依次为1-N 假设我们需要将员工信息编码 则可以将id、employee、address存进XmlDictionaryString中 由于每个Xml元素对应着XmlDictionaryString中的Value 所以编码时可以将这些名称替换为XmlDictionaryString的Key

XmlDictionaryWriter

此类实现对Xml的编码 它是一个抽象类 提供一系列静态的CreateXxx方法来创建编码对象

XmlDictionaryWriter.CreateTextWriter()

此方法基于纯文本进行编码 具有多个重载版 这里列出其所有的参数进行说明 参数如下

stream:Stream类型的、表示数据流的对象 编码后数据将写入此流

encoding:支持UTF-8和Unicode两种编码方式 默认值是UTF-8

ownsStream:布尔值 表明创建的编码对象是否拥有对应的Stream对象 如果是 则关闭编码对象对象时 相应的流也会自动关闭 默认值为true

 Employee employee = new Employee
{
ID = "",
Gender = "男",
Name = "sam",
Department = "IT"
}; using (MemoryStream stream = new MemoryStream())
{
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Employee));
serializer.WriteObject(writer, employee);
} long count = stream.Position;
byte[] bytes = stream.ToArray();
StreamReader reader = new StreamReader(stream);
stream.Position = ;
string content = reader.ReadToEnd(); Console.WriteLine("字节数:{0}\n", count);
Console.WriteLine("编码后的二进制表示:{0}\n", BitConverter.ToString(bytes));
Console.WriteLine("编码后的文本表示:{0}", content);
Console.Read();
}

结果

 字节数:

 编码后的二进制表示:3C--6D--6C-6F------6D-6C-6E--3D-----
-3A-2F-2F-----6D---2E------6F-6E------2E-6F-
--2F-----2F---2F--6C---6E-----6D-6C-6E--3A--3D-
-----3A-2F-2F----2E---2E-6F---2F-----2F--4D-4C-
----6D--2D--6E----6E----3E-3C-------6D--6E
--3E---3C-2F-------6D--6E--3E-3C---6E----3E-E7-
-B7-3C-2F---6E----3E-3C---3E----3C-2F---3E-3C-4E--6D-
-3E---6D-3C-2F-4E--6D--3E-3C-2F--6D--6C-6F----3E 编码后的文本表示:<Employee xmlns="http://schemas.datacontract.org/2004/07/Clien
t" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Department>IT</Departmen
t><Gender>男</Gender><ID></ID><Name>sam</Name></Employee>

XmlDictionaryWriter.CreateBinaryWriter()

此方法基于二进制进行编码 具有多个重载版 这里列出其所有的参数进行说明 参数如下

stream:Stream类型的、表示数据流的对象 编码后数据将写入此流

dictionary:一个XmlDictionary类型的对象 前面我们说通过XmlDictionary可以表示编码解码的词汇表 提供一个词汇表 则可以使编码后的字节数更少

session:一个XmlBinaryReaderSession类型的对象 允许以动态的方式管理经过优化的字符

ownsStream:布尔值 表明创建的编码对象是否拥有对应的Stream对象 如果是 则关闭编码对象对象时 相应的流也会自动关闭 默认值为true

dictionary配合session使用 则会发挥编码后字节节省量的威力 通过下面的例子可以说明使用dictionary配合session和不使用这两个参数时编码后的字节量的大小

 using (MemoryStream stream = new MemoryStream())
{
XmlBinaryWriterSession session = new XmlBinaryWriterSession();
XmlDictionary dictionary=new XmlDictionary();
dictionary.Add("ID");
dictionary.Add("Gender");
dictionary.Add("Department"); using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream,dictionary,session,false))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Employee));
serializer.WriteObject(writer, employee);
} long count = stream.Position;
byte[] bytes = stream.ToArray();
StreamReader reader = new StreamReader(stream);
stream.Position = ;
string content = reader.ReadToEnd(); Console.WriteLine("字节数:{0}\n", count);
Console.WriteLine("编码后的二进制表示:{0}\n", BitConverter.ToString(bytes));
Console.WriteLine("编码后的文本表示:{0}", content);
Console.Read();
}

当使用词汇表和session时 编码字节量最少 结果如下

 字节数:
编码后的二进制表示:--0A--0B--------------E7--
B7--0B-------0D-----6D-

不使用时 则结果为

 字节数:
编码后的二进制表示:---6D--6C-6F-----2E-----3A-2F-2F--
---6D---2E------6F-6E------2E-6F---2F---
--2F---2F--6C---6E----------3A-2F-2F----2E-
--2E-6F---2F-----2F--4D-4C-----6D--2D--6E----
6E----0A-------6D--6E----------6E---
---E7--B7------------4E--6D------6D-

XmlDictionaryWriter.CreateMtomWriter()

此方法基于MTOM进行编码 当使用此方法对消息进行编码 最终会生成一个具有报头和主体的MIME Multipart/Related XOP数据包 XML内容经过编码被放到主体部分 此方法具有多个重载版 这里列出其所有的参数进行说明 参数如下

stream:Stream类型的、表示数据流的对象 编码后数据将写入此流

encoding:支持UTF-8和Unicode两种编码方式 默认值是UTF-8

ownsStream:布尔值 表明创建的编码对象是否拥有对应的Stream对象 如果是 则关闭编码对象对象时 相应的流也会自动关闭 默认值为true

maxSizeInBytes:

startInfo:表示被编码的xml对应的Content-Type的Type属性

boundary:表示被编码的xml的分隔符

startUri:表示被编码的xml对应的Content-ID

writeMessageHeaders:是否写入MIME Multipart/Related XOP数据包的报头内容

 using (MemoryStream stream = new MemoryStream())
{
string startInfo = "application/soap+xml";
string boundary = "http://www.cnblogs.com/boundary";
string startUri = "http://www.cnblogs.com/contentid"; using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateMtomWriter(stream,Encoding.UTF8,int.MaxValue,startInfo,boundary,startUri,true,false))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Employee));
serializer.WriteObject(writer, employee);
} long count = stream.Position;
byte[] bytes = stream.ToArray();
StreamReader reader = new StreamReader(stream);
stream.Position = ;
string content = reader.ReadToEnd(); Console.WriteLine("字节数:{0}\n", count);
Console.WriteLine("编码后的二进制表示:{0}\n", BitConverter.ToString(bytes));
Console.WriteLine("编码后的文本表示:{0}", content);
Console.Read();
}

输出结果比较冗余 因为此方法基于MTOM只针对大型二进制数据的编码才会发挥优化数据的能力

 字节数:

 编码后的二进制表示:4D--4D--2D------6F-6E-3A---2E--0D-0A--
6F-6E---6E--2D-----3A--6D--6C-------2F---6C-
----3B-----3D-----6C------6F-6E-2F--6F--2B-
-6D-6C--3B--6F--6E-----3D------3A-2F-2F----2E--
6E--6C-6F---2E--6F-6D-2F--6F--6E------3B------3D
--3C-----3A-2F-2F----2E--6E--6C-6F---2E--6F-6D-2F--
F-6E---6E----3E--3B------2D--6E--6F-3D-----6C-
-----6F-6E-2F--6F---2B--6D-6C--0D-0A-0D-0A-2D-2D----
-3A-2F-2F----2E--6E--6C-6F---2E--6F-6D-2F--6F--6E----
-0D-0A--6F-6E---6E--2D---3A--3C-----3A-2F-2F----2E-
-6E--6C-6F---2E--6F-6D-2F--6F-6E---6E----3E-0D-0A--6F-6E
---6E--2D----6E-----2D--6E--6F---6E--3A----
--0D-0A--6F-6E---6E--2D-----3A-----6C------
6F-6E-2F--6F--2B--6D-6C-3B--------3D----2D--3B--
---3D-----6C------6F-6E-2F--6F---2B--6D-6C--0D-
A-0D-0A-3C--6D--6C-6F------6D-6C-6E--3D------3A-2F-2F-
----6D---2E------6F-6E------2E-6F---2F--
---2F---2F--6C---6E-----6D-6C-6E--3A--3D-----
-3A-2F-2F----2E---2E-6F---2F-----2F--4D-4C-----
6D--2D--6E----6E----3E-3C-------6D--6E--3E--
-3C-2F-------6D--6E--3E-3C---6E----3E-E7--B7-3C-2F-
--6E----3E-3C---3E----3C-2F---3E-3C-4E--6D--3E---
6D-3C-2F-4E--6D--3E-3C-2F--6D--6C-6F----3E-0D-0A-2D-2D----
-3A-2F-2F----2E--6E--6C-6F---2E--6F-6D-2F--6F--6E----
-2D-2D-0D-0A 编码后的文本表示:MIME-Version: 1.0
Content-Type: multipart/related;type="application/xop+xml";boundary="http://www.
cnblogs.com/boundary";start="<http://www.cnblogs.com/contentid>";start-info="app
lication/soap+xml" --http://www.cnblogs.com/boundary
Content-ID: <http://www.cnblogs.com/contentid>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-;type="application/soap+xml" <Employee xmlns="http://schemas.datacontract.org/2004/07/Client" xmlns:i="http:/
/www.w3.org//XMLSchema-instance"><Department>IT</Department><Gender>男</Gend
er><ID></ID><Name>sam</Name></Employee>
--http://www.cnblogs.com/boundary--

XML解码

XmlDictionaryReader类

此类用于对消息进行解码 还原编码前的XML数据 此类具有如下静态方法

XmlDictionaryReader.CreateTextReader()

对基于纯文本的数据进行解码 具有多个重载版 这里列出其所有的参数进行说明 参数如下

stream:Stream类型的、表示数据流的对象 解码后数据将写入此流

encoding:支持UTF-8和Unicode两种编码方式 默认值是UTF-8

quotas:一个System.Xml.XmlDictionaryReaderQuotas对象 该对象提供5个属性 MaxArrayLength表示允许的最大数组长度 默认值16384 MaxBytesPerRead表示每次读取的最大字节数 默认值4096 MaxDepth表示节点最大嵌套深度 默认值32 MaxStringContentLength表示读取的字符串的最大长度 默认值8192 MaxNameTableCharCount表示表中允许的最大字符数 默认值16384 quotas主要用于限制客户端恶意攻击 传输大体量的数据包让服务端解码 耗费服务端计算资源

buffer:读取数据的缓冲区

XmlDictionaryReader.CreateBinaryReader()

对基于二进制的数据进行解码 具有多个重载版 所有参数同上

XmlDictionaryReader.CreateMtomReader()

对基于基于MTOM进行编码的数据进行解码 具有多个重载版 所有参数同上

编码解码绑定元素

我们说绑定由绑定元素构成 而编码解码的绑定元素则有三种类型 它们是:

BinaryMessageEncodingBindingElement类

此类表示基于二进制的编码绑定元素

TextMessageEncodingBindingElement类

此类表示基于纯文本的编码绑定元素

MtomMessageEncodingBindingElement类

此类表示基于MTOM的编码绑定元素

WCF对消息进行编码解码时 是采用的以上哪种类型的编码绑定元素呢? 这主要是看WCF使用的是何种类型的绑定对象 如果是以Net开头的绑定对象采用的编码解码绑定元素是BinaryMessageEncodingBindingElement类 而BasicHttpBinding和以WS开头的绑定对象采用的编码解码绑定元素则是TextMessageEncodingBindingElement类和MtomMessageEncodingBindingElement类 而WCF采用的又是何种类型的编码解码器呢 则由绑定的编码解码绑定元素来决定 以上三种类型的编码解码绑定元素都派生于抽象基类MessageEncodingBindingElement类 此类提供抽象方法CreateMessageEncoderFactory 此方法用于返回一个用于创建编码解码器的MessageEncoderFactory编码解码器工厂对象 根据不同类型的编码解码绑定元素 编码解码器工厂调用自身的Encoder属性来返回对应的编码解码器对象

编码解码器

MessageEncoder

ns:System.ServiceModel.Channels

WCF最终是通过编码解码器来实现对消息的编码解码的 编码解码器由MessageEncoder类表示 它提供了以下两个方法用于消息的编码与解码

ReadMessage()

解码消息 此方法具有多个重载版 这里列出其所有的参数进行说明 参数如下

stream:从指定的流中读取消息的流对象

maxSizeOfHeaders:从消息中读取的报头的最大大小

buffer:从指定的缓冲区中读取消息的缓冲区对象

bufferManager:管理缓冲区的对象

WriteMessage()

编码消息 此方法具有多个重载版 这里列出其所有的参数进行说明 参数如下

message:消息对象

stream:将消息编码后写入的流对象

maxMessageSize:可写入的最大消息大小

bufferManager:管理缓冲区的对象

编码解码器工厂

MessageEncoderFactory

ns:System.ServiceModel.Channels

编码器是通过编码器工厂创建的 编码器工厂使用MessageEncoderFactory来表示 它提供Encoder属性来获取一个具体的消息编码解码器 而编码解码器工厂是个抽象类 不能直接创建 此工厂类是通过编码解码绑定元素的CreateMessageEncoderFactory来创建的 即具体的编码解码绑定元素通过调用自身的一个方法来创建编码解码器工厂对象 使用编码解码器工厂对象来创建具体的编码解码器对象 如

 using System.ServiceModel.Channels;

 BinaryMessageEncodingBindingElement encoderBindingElm = new BinaryMessageEncodingBindingElement();//基于二进制的编码解码绑定元素
MessageEncoderFactory encoderFactory = encoderBindingElm.CreateMessageEncoderFactory();//创建编码解码器工厂
MessageEncoder encoder = encoderFactory.Encoder;//创建编码解码器

配置自定义编码解码绑定元素

对于自定义绑定来说 我们可以自由组合构成绑定的绑定元素 这意味着可以选择我们需要的编码解码绑定元素 一般通过配置的方式来实现 自定义绑定的xml元素是<bindings>的子元素<customBinding> textMessageEncoding配置元素则对应了编码解码的绑定元素TextMessageEncodingBindingElement

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding>
<textMessageEncoding maxReadPoolSize="" maxWritePoolSize=""/>
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>

binaryMessageEncoding配置元素对应了编码解码的绑定元素BinaryMessageEncodingBindingElement

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding>
<binaryMessageEncoding maxReadPoolSize="" maxWritePoolSize="" maxSessionSize=""/>
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>

mtomMessageEncoding配置元素对应了编码解码的绑定元素MtomMessageEncodingBindingElement

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding>
<mtomMessageEncoding maxReadPoolSize="" maxWritePoolSize="">
<readerQuotas maxArrayLength="" maxBytesPerRead="" maxDepth=""
maxStringContentLength="" maxNameTableCharCount=""
/>
</mtomMessageEncoding>
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>

WCF - 学习总目录

WCF - 消息的更多相关文章

  1. WCF初探-4:WCF消息交换模式之请求与答复模式

    请求与答复模式( Request/Reply) 这种交换模式是使用最多的一中,它有如下特征: 调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型 相比Duplex来讲,这种模式强调的 ...

  2. WCF初探-19:WCF消息协定

    WCF消息协定概述 在生成 WCF应用程序时,开发人员通常会密切关注数据结构和序列化问题,而不必关心携带数据的消息结构. 对于这些应用程序,为参数或返回值创建数据协定的过程很简单.但是,有时完全控制 ...

  3. 替换 wcf 消息传输中的 命名空间

    替换 wcf 消息传输中的 命名空间,http://vanacosmin.ro/Articles/Read/WCFEnvelopeNamespacePrefix

  4. WCF消息之XmlDictionaryWriter

    原文:WCF消息之XmlDictionaryWriter XmlDictionaryWriter,是一个抽象类,从该类中派生了WCF,以便执行序列化和反序列化. 它有4种格式书写器: CreateBi ...

  5. WCF消息交换模式之双工通讯(Duplex)

    WCF消息交换模式之双工通讯(Duplex) 双工通讯Duplex具有以下特点: 1它可以在处理完请求之后,通过请求客户端中的回调进行响应操作 2.消息交换过程中,服务端和客户端角色会发生调换 3.服 ...

  6. Prism for WPF 搭建一个简单的模块化开发框架(四)异步调用WCF服务、WCF消息头添加安全验证Token

    原文:Prism for WPF 搭建一个简单的模块化开发框架(四)异步调用WCF服务.WCF消息头添加安全验证Token 为什么选择wcf?   因为好像wcf和wpf就是哥俩,,, 为什么选择异步 ...

  7. WCF初探-3:WCF消息交换模式之单向模式

    单向模式(One-Way Calls): 在这种交换模式中,存在着如下的特征: 只有客户端发起请求,服务端并不会对请求进行回复 不能包含ref或者out类型的参数 没有返回值,返回类型只能为void ...

  8. WCF初探-5:WCF消息交换模式之双工通讯(Duplex)

    双工通讯Duplex具有以下特点: 1它可以在处理完请求之后,通过请求客户端中的回调进行响应操作 2.消息交换过程中,服务端和客户端角色会发生调换 3.服务端处理完请求后,返回给客户端的不是reply ...

  9. WCF 消息压缩性能问题及解决方法

    最近使用WCF作为通迅框架开发一套信息系统,系统使用传统C/S框架,系统有可能会部署在互联网上,因此决定对传输的数据进行GZIP压缩,原来在使用.NET Remoting时,可以使用插入自定义的Cha ...

随机推荐

  1. 【转】Spring 注解学习手札(超好的springmvc注解教程)

    Spring 注解学习手札(一) 构建简单Web应用 Spring 注解学习手札(二) 控制层梳理 Spring 注解学习手札(三) 表单页面处理 Spring 注解学习手札(四) 持久层浅析 Spr ...

  2. ThreadLocal学习

    1.简介: 类ThreadLocal<T>,为变量提供了线程本地化副本.对于用ThreadLocal维护的变量,当前线程中的副本不同于它在其他线程中的副本,每个线程通过ThreadLoca ...

  3. PYTHON常见数据类型示例

    shoplist = ['apple', 'mango', 'carrot', 'banana'] print('I have ', len(shoplist), ' items to purchas ...

  4. 详解如何用AD 生成Gerber文件

    以上gerber文件就出完了;  下面步骤是:进行导出" 钻孔文件 ". 以上钻孔文件就出完了;  到此就全部完成输出了. 下面的操作,也可以不用导的 .下面步骤是:进行导出&qu ...

  5. redis的图形界面管理工具:phpredisadmin

    大部分人都知道redis是一款用在缓存服务器上的软件,它与memcache类似,都可以存储海量的数据,用在大访问量的web网站.聊天记录存放等方面,但是又与memcache不同: 1.缓存数据可以持久 ...

  6. 【HDOJ】1239 Calling Extraterrestrial Intelligence Again

    这题wa了很多词,题目本身很简单,把a/b搞反了,半天才检查出来. #include <stdio.h> #include <string.h> #include <ma ...

  7. linux下jdk的卸载与安装

    JDK的卸载 1.检查jdk的是否安装,显示如下表示安装: [root@localhost ~]# rpm -aq|grep java tzdata-java-2010l-1.el6.noarch j ...

  8. Modifying the ASP.NET Request Queue Limit

    Modifying the ASP.NET Request Queue Limit When ASP.NET is queried, the request for service is carrie ...

  9. 常用开源GIS项目

    常用开源GIS项目     常用开源桌面GIS软件 QGIS 始于2002年5月,算得上是开源GIS平台中的后起之秀.界面友好,分析功能可与GRASS GIS相媲美.主页:http://www.qgi ...

  10. python进度1

    Python 错误和异常 异常参数: 3.4与2.7有些不同 3.4中 try: x except NameError as e: print(type(e)) print(e) 运行结果: < ...