版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、wcf技术剖析之十七:消息(message) 详解(下篇)爱心链接:拯救一个25岁身患急性白血病的女孩内有苏州电视台经济频道天 天山海经为此录制的节口视频(苏州话)wcf技术剖析(卷1)自出版 近20天以来,得到了园子里的朋友和广大wcf爱好者的一致好评,并被卓越网 计算机书店作为首页推荐,在这里对大家的支持表示感谢。同时我将一直坚持这 个博文系列,与大家分享我对wcf 些感悟和学习经验。在消息(message) 详解系列的上篇和中篇,先后对消息版本、详细创建、状态机和基于消息的基 本操作(读取、写入、拷贝、关闭)进行了深入剖析,接下来我们来谈谈消息的 另一个重要组成部分:消息报头(messa
2、ge header) o按照s0ap1. 1或者s0ap1. 2规范,一个soap消息由若干soap报头和一个soap 主体构成,soap主体是soap消息的有效负载,一个soap消息必须包含一个唯 一的消息主体。soap报头是可选的,一个soap消息可以包含一个或者多个soap 报头,soap报头一般用于承载一些控制信息。消息一经创建,其主体内容不能 改变,而soap报头则可以自由地添加、修改和删除。正是因为soap的这种具冇 高度可扩展的设计,使得soap成为实现soa的首选(有这么一种说法soap= soa protocol)。按照soap 1. 2规范,一个soap报头集合出一系列xm
3、l元索组成,每一个报头元 素的名称为 header,命名空间为 http:/www.w3. org/2003/05/soap-envclopco 每一个报头元素可以包含任意的屈性(attribute)和子元素。在wcf屮,定义 了一系列类型用于表示soap报头。、 messageheaders> messageheaderlnfo、 messageheader 和messageheader<t>在message类中,消息报头集合通过只读属性headers表示,类型为system. servicemodel. channels. messageheaderso messageh
4、eaders 木质上就是 一个 system. servicemodel. channels. messageheadertnfo 集合。1: public abstract class message : idisposable2: 3:/其他成员4:public abstract messageheaders headers get; 5: 1: public sealed class messageheaders : ienumerable<messageheaderinfo>, ienumerable3:/省略成员4: messageheaderlnfo是一个抽象类型,是所
5、有消息报头的基类,定义了一系列消 息soap报头的基木属性。其屮name和namespace分别表示报头的名称和命名空 间,actor> mustunderstand> reply 与 soap 1.1 或者 soap 1.2 规定 soap 报头 同名屈性对应。需要对soap规范进行深入了解的读者可以从w3c官方网站卜载 相关文档。1: public abstract class messageheaderlnfo2: 3 :protected messageheaderlnfo ();4 :5:publicabstractstringactor get; 6:publicabs
6、tractboolisreferenceparameter get;7:publicabstractboolmustunderstand get; 8:publicabstractstringname get; 9:publiczbstractstringnamespace get; 10:publicabstractboolrelay get; 11: 当我们针对消息报头编程的时候,使用到的是另一个继承自messageheaderlnfo 的抽象类:system. servicemodel. channel s. messageheadero 除了实现 messageheaderlnfo定义
7、的抽象只读屈性外,mcssagciiceidcr中定义了一系列工 丿方法(createheader)方便开发人员创建messageheader对彖。这些 createheader方法接受一个可序列化的对彖,并以此作为消息报头的内容,wcf 内部会负责从对象到xml infoset的序列化工作。此外,可以通过相应的 wri teheader方法对messageheader对象执行写操作o messageheader定义如下:1:opublic abstract class messageheader : messageheaderlnfo乙3:public static messageheade
8、r createheader(string narnef string ns, object value);4:public static messageheader createheader(string name, string ns, object value,boolmustunderstand);5:/其他 createheader 方法o 7:public void writeheader(xmldictionarywriter writer, messageversionmessageversion);8:public void writeheader(xmlwriter wri
9、ter, messageversion messageversion);9:/其他 writeheader 方法10:11:public override string actor get; )12:public override bool isreferenceparameter get; 13:public override bool mustunderstand get; 14:public override bool relay get; 除了 messageheader, wcf还提供一个非常有价值的泛型类:system. servicemodel. messageheader<
10、;t>,泛型参数t表示报头内容对应的类 型,mess3geheeidert>为我们提供了强类型的报头创建方式。曲于message的 headers 屈性是一个 mcssagellcadcrlnfo 的集合,mcssagciicadcrt>并不能直接 作为message对象的消息报头。getuntypedheader方法捉供了从messageheader<t>对象到 messageheader 对象的转换o messageheader<t>定义如 下:1:public class messageheader<t>2:3:public mess
11、ageheader();4:public messageheader(t content);5:public messageheader (t content, bool mus tunders tand, st ring actoi:' bool relay);6:7 public messageheader getuntypedheader(string name, string ns);r 8:public string actor get; set; 9:public t content get; set; 10:public bool mustunderstand get;
12、set; 11:public bool relay get; set; 12:接下来,我们通过一个简单的例子演示如何为一个message对象添加报头。假设 在一个wcf应用中,我们需要在客户端和服务端之间传递一些上下文(context) 的信息,比如当前用户的相关信息。为此我定义一个applicationcontext类, 这是一个集合数据契约(关于集合数据契约,可以参考我的文章:泛型数据契约 和集合数据契约)。applicationcontcxt是一个字典,为了简单起见,key和 value均使用字符串。applicationcontext不能被创建(构造函数被私有化), 只能通过静态只读属
13、性current得到。当前applicationcontext存入 callcontext从而实现了在线程范围内共享的目的。在applicationcontext中 定义了两个属性username和department,表示用户名称和所在部门。3个常量 分别表示applicationcontcxt存储于callcontext的key,以及置于 messageheader后对应的名称和命名空间。1: collectiondatacontract(namespace = n itemname = "contextnzkeynair.e = nkeyn, valuename = nvalu
14、en)2: public class applicationcontext : dietionary<string, string>4:private const string callcontextkey = n_applicationcontextn;5:public const string headerlocalname = napplicationcontextn;6 :public const string headernamespace = nhttp : /www. artech . com/'1;8:privateapplicati9: 10:11:pub
15、licstatic applicationcontext. current12:13:get14:15:if (callcontext getdata(callcontextkey) = null)16:17:callcontext.setdata(callcontextkey, nev? applicationcontext();18:19:return (applicationcontext)callcontext.getdata(callcontextkey);20:)21:22:23:publicstring username24:25:get26:27:if (!this.conta
16、inskey(nusernamen)28:29:return string empty;30:31:return thisnjsernamen;32:33:set34:35:this n_username11 = value;36:37:38:39:publicstring department40:41:get42:43:if (!this >containskey(hdepartment”)44:45:return string.empty;46:47:return this ndepartnentin ;48:49:set50:51:this ndepartment11 = val
17、ue;在下面代码中,首先对当前applicationcontext进行相应的设置,然后创建 messageheader<appl icationcontext>对象。通过调用 getuntypedheader 转换成 mcssagellcadcr对象z后,将其添加到message的headers屈性集合中。后面是 生成的soap消息。1: message message = message.createmessoge(messageversiondefault, n2: applicationcontext current username = nfoon;3 : applicat
18、ioncontext current department = m itr,;4: messageheader<applicationconrext> header = newmessageheader<applicationcontext>(applicationcontext current);5: message headers add(header getuntypedheader(applicationcontext headerlocalname, applicationcontext.headernamespace);6: writemessage(mes
19、sage, me:message.xmln);1:<s :envelope xmlns :a=hhttp: /www. w3 org/2005/08/addressing1'xmlns:s=,http: /www w3 org/2003/05/soap-envglope”>2:<s:header>3:<a:action s:mustunderstand=n1n>http:/www mitech com/myaction</a:action>4 :<applicationcontext xmlns:i=hhttp:/wwww3org/
20、2001/xmlschema-instancemxmlnsmhttp:/www.artech com/n>5 :<context>6:<key>_username</key>7:<value>foo</value>8:</context>9:<context>10:<key>_department</key>11:<value>it</value>12:</context>13:</applicationcontext>14:<
21、/s:header>15:<s:body />16:</s:envelope>实例演示:通过消息报头传递上下文信息在演示添加消息报头的例子屮,创建了一个app 1 icationcontcxt,这个类型将 继续为本案例服务。上面仅仅是演示如果为一个现成的message对象添加相应的报头,在本例屮,我们将演示在一个具体的wcf应用屮如何通过添加消息报头的 方式从客户端向服务端传递一些上下文信息。上面我们定义的appl icat ioncontext借助于cal 1 context实现了同一线程内数 据的上下文消息的共享。由于callcontext的实现方式是将数据存
22、储于当前线程 的tls (thread local storage)中,所以它仅仅在客户端或者服务端执彳亍的线 程屮有效。现在我们希槊相同的上下文信息能够在客户端和服务端之间传递,毫 无疑问,我们只有唯一的办法:就是将信息存放在请求消息和冋复消息中。图1 大体上演示了具体的实现机制。客户端的每次服务调用,会将当h'j applicationcontext圭寸装成messageheader, 存放到出栈消息(outbound message)的soap报头中;服务端在接收到入栈消 息(inbound message)后,将其取出,作为服务端的当前applicationcontexto 由
23、此实现了客户端向服务端的上下文传递。从服务端向客户端上下文传递的实现 与此类似:服务端将当前applicationcontext植入出栈消息(outbound message) 的soap报头中,接收到该消息的客户端将其取出,覆盖掉现冇上下文的值。图1上下文信息传递在消息交换中的实现我们知道了如何实现消息报头的创建,现在需要解决的是如何将创建的消息报头 植入到出栈和入栈消息报头集合屮。我们可以借助system. servicemodel. operationcontext 实现这样的功能。operationcontext 代表当前操作执行的上下文,定义了一系列与当前操作执行有关的上卜文展性,
24、其屮就包含出栈和入栈消息报头集合。对于一个请求-回复模式服务调用来讲, incomingmessageheaders 和 outgoingmessageheaders 对于客户端分别代表冋复 和请求消息的soap报头,对于服务端则与此相反。注:operationcontext代表服务操作执行的上下文。通过operationcontext可以得到出栈和入 栈消息的soap报头列表、消息属性或者http报头。对于duplex服务,在服务端可以通过operationcontext得到回调对象。此外通过operationcontext还可以得到基于当前执行的安全方面的属性一起的其他相关信息。1: pu
25、blic sealed class operationcontext : iextensibleobject<operationcontext>2: 3:/其他成员4:public messageheaders5:public messagelieaders6:)incomingmessageheaders get; outgoingmessagelleaders get;)冇了上而这些铺垫,对于我们即将演示的案例就很好理解了。我们照例创建一个 简单的计算器的例子,同样按照我们经典的4层结构,如图2所示。图2上下文传递案例解决方案结构先看看服务契约(calculator)和服务实现
26、(calculatorservice)。在add 操作的具体实现中,先通过operationcontext. current. incomingmessageheaders, 根据预先定义在 applicationcontext中的报头名称和命名空间得到从客户端传入的 applicationcontext,并将其输出。待运算结束后,修改服务端当而 applicationcontext 的值,并将其封装成 messageheader,通过 operationcontext. current. outgoingmessageheaders 植入到冋复消息的 soap 报头中。1: using sy
27、stem.servicemodel;2: namespace artech.contextpropagation.contracts3: 4 :servicecontract5 :public interface icalculator6:(7:operationcontract8 :double add(double x, double y);1: using system;2: using artech contextpropagation contracts;3: using system servicemodel;4: namespace artech.contextpropagati
28、on.services5: 6:public class calculatorservice : icalculator7:8:public double add(double x, double y)9:10 :/从请求消息报头屮获取applicationcontext11:applicationcontext context =operationcontext current incomingmessageheaders getheader<applicationcontext>(applicat ioncontext headerlocalname, applicationc
29、ontext headernamespace);12:applicationcontext.current.username = contextusername;13:applicationcontextcurrent department = contextdepartment;14:console.writeline(napplicationcontext current username = n0n n/applicationcontext.current username);15:console.writeline(napplicationcontext current departm
30、ent = n0)n n,applicationcontext.current.department);16:17 :double result = x + y;18:19:/将服务端当iju applicationcontext添加到回复消息报头集合20 :applicationcontext .current .username =;21:applicationcontextcurrentdepartment = nhr/admin"22 :messageheader<applicationcontext> header = newmessageheader<a
31、pplicalioncontexl>(applicarioncontext current);23:operationcontextcurrent.outgoingmessageheaders.add(header.getuntypedheader(applicationcon匸extheaderlocalname,applicationcontextheadernamespace);24:25:return result;26:27:)28: 客户端的代码与服务端在消息报头的设置和获取止好相反。在服务调用代码屮, 先初始化当前 applicationcontext,通过 channel
32、factory<icalculator>创建服 务代理对象。根据创建的服务代理对象创建operationcontextscope对象。在该 operationcontextscope对象的作用范围内(using块中),将当前的 applicationcontext封製成mcssageheader并植入出栈消息的报头列表屮,待正确返回执行结果后,获取服务端植入回复消息屮返回的appicationcontext, 并覆盖掉现有的context相应的值。注:同 transaction 和 transactionscope 样,operationcontextscope 定义了 当前ope
33、rationcontext存活的范围。对于客户端来说,当前的0perationcontext生命周期和operationcontextscope 样,一旦成功创建 0perationcontextscope ,就会创建当前的operationcontext ,当 operationcontextscope 的 dispose 方法被执行,当前的operationcontext对象也相应被回收。1:usingsystem;2:usingartech contextpropagation contracts;3:usingsystem.servicemodel;4:usingsystem.serv
34、icemodel.cha rmels;namespace artech.contextpropagation6:7:class program8:9:static void main(string args)10:11:applicationcontext current username = ”foo"12:applicationcontext current department = nlln;13:using (channelfactory<icalculator> channelfactozy = newchannelfactory<icalculator&
35、gt;(hcalculatorservicen)14:15:icalculator calculator = channelf&ctorycreatechannel ();16:using (calculator as idisposable)17:18:using (operationcontextscope contextscope = newoperationcontextscope (calculator as icontextchannel)19:20:/将客户端当前applicationcontext添加到请求消息报头集合21:messageheader<applicationcontext> header = newmessageheader<applicationcontext>(applicationcontext.current);22:operationcontext current outgoingmessageheaders add(header.getuntypedhe&der(applicatio ncontext headerlocalname, applicationcontext headernamespace);23 :console.writeline
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 老旧片区共享花园更新项目可行性研究报告
- 智慧路灯控制系统施工方案
- 设备检修现场安全管理制度
- 旧厂房雨水花园项目可行性研究报告
- 基本公卫协管考核评估实施方案
- 轻钢龙骨建筑防火施工方案
- 农药使用安全培训课件
- 老旧片区智慧安防升级项目可行性研究报告
- 土石方工程弃土场安全操作手册
- 历史街区微更新项目可行性研究报告
- 新时代陕西省立德树人工作指南细则
- GB/T 1539-2026纸板耐破度的测定
- 继发性甲状旁腺功能亢进
- 《鸦胆子油乳注射液治疗消化道恶性肿瘤临床应用专家共识-公示稿》
- 2025-2030株洲轨道交通行业竞争格局分析及智能化升级研究
- dbj41现浇混凝土内置保温墙体技术规程
- 深度解析(2026)《HJT 81-2001畜禽养殖业污染防治技术规范》
- 消防安全培训医院课件
- 化学检验员高级工练习题库含参考答案
- 2026年淮北矿业集团所属企业招聘备考题库及完整答案详解
- 注意缺陷多动障碍(ADHD)法律能力评估与支持方案
评论
0/150
提交评论