博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【WCF】为终结点地址应用地址头
阅读量:6709 次
发布时间:2019-06-25

本文共 6421 字,大约阅读时间需要 21 分钟。

记得不久前,老周写过博文,探讨过在ContextScope以一定的范内向发出的消息中插入消息头,scope只能为特定的某一次服务操作的调用而添加SOAP头,要是需要在每次调用操作协定的时候都插上Header,一种方法可以自定义实现消息拦截器,拦截服务传输的消息,并向消息添加header,关于拦截器,老周后面会介绍。另一种方法,就是本文的主题——地址头。

我们都知道,服务对外公开后,客户端可以把消息传输到终结点指定的监听URI上,终结点收到消息后,通过Dispatcher把消息调度到对应用的通道(channel)上,最后找到对应的服务协定,并将收到的输入参数传给服务操作,进而调用服务。服务调用后,操作结果会沿着相反的顺序传回客户端,即返回的内容被包装为SOAP消息,放入通道栈,再输送回调度程序,最后发回客户端。

终结点以Binding确定通信协议和方式,并必须指定一个有效的地址来接收调用消息,为了能够调用服务操作,还得指定一个服务协定。如果为终结点的地址加上header,那么,只要服务是通过该终结点调用的,则每一次调用都会自动把地址头插入到传出的消息中。这样我们就不必要每次调用都去添加消息头了。

 

不知不觉就说了一堆F话,下面,咱们来做一个例子,这个例子是通过代码来指定地址头。

首先,当然是声明服务协定。

[ServiceContract]    public interface IDemo    {        [OperationContract]        string Hello(string name);    }

 

然后,实现服务类。

class MyService : IDemo    {        public string Hello(string name)        {            return $"Hello, {name}.";        }    }

服务的实现比较简单,我们的重点不是这些,所以随意弄弄。

 

接下来就是重点了,咱们用代码来添加地址头。地址头可以添加任意内容,最终会被序列化为XML元素,插入到消息头列表中。假设,我们定义一个名为AddressInfo的数据协定。

[DataContract(Namespace = "addr-info", Name = "addr_info")]    public class AddressInfo    {        ///         /// 省        ///         [DataMember(Name = "province")]        public string Province { get; set; }        ///         /// 市        ///         [DataMember(Name = "city")]        public string City { get; set; }        ///         /// 详细地址        ///         [DataMember(Name = "details")]        public string DetailAddress { get; set; }        ///         /// 邮政编号        ///         [DataMember(Name = "zipcode")]        public string ZipCode { get; set; }    }

 

最后建立服务寄宿,并添加终结点。

Uri endpointAddress = new Uri("http://localhost:2016/demo");            using(ServiceHost host=new ServiceHost(typeof(MyService)))            {                // 添加终结点,必须在Open之前完成。                // Open之后修改就不会生效了。                // 1、准备地址头                AddressInfo addrinfo = new AddressInfo                {                    Province = "广东省",                    City = "东莞市",                    ZipCode = "523xxx",                    DetailAddress = "火星镇XX小区非人道主义研究大厦K008室"                };                // 创建Header                AddressHeader hd = AddressHeader.CreateAddressHeader(addrinfo);                // 2、创建EndpointAddress                EndpointAddress epaddress = new EndpointAddress(endpointAddress, hd);                // 3、创建终结点实例                BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);                ServiceEndpoint svendp = new ServiceEndpoint(ContractDescription.GetContract(typeof(IDemo)), binding, epaddress);                // 4、添加终结点到host中                host.AddServiceEndpoint(svendp);                // 5、打开服务                try                {                    host.Open();                    Console.WriteLine("服务已启动。");                }                catch(Exception ex)                {                    Console.WriteLine(ex.Message);                }                Console.Read();            }

AddressHeader类表示一个消息头,它是抽象类,提供内部实现,使用时应调用它公开的CreateAddressHeader静态方法来得到一个AddressHeader实例,参数必须是一个可以XML序列化的对象,这里用的是我刚刚定义的数据协定,它当然可以XML序列化了。

如果是要创建带有一些基本类型值的头,可以这样用:

AddressHeader hd3 = AddressHeader.CreateAddressHeader("element", "my-namespace", 300);

第一个参数是XML元素的名字,第二个参数是XML命名空间,一般以URI形式表示,当然你可以随便写,只要不带非法字符就行。第三个参数是Header中的内容,即value,这里用的是整数值300。

 

地址头可以在实例化EndpointAddress时通过构造函数传递。

EndpointAddress epaddress = new EndpointAddress(endpointAddress, hd);

 

下面,我们来调用一下服务。

// 1、准备地址头                AddressInfo cl_addrinfo = new AddressInfo();                cl_addrinfo.Province = "广东省";                cl_addrinfo.City = "东莞市";                cl_addrinfo.DetailAddress = "火星镇XX小区非人道主义研究大厦K008室";                cl_addrinfo.ZipCode = "523xxx";                AddressHeader hdclient = AddressHeader.CreateAddressHeader(cl_addrinfo);                // 2、创建终结点地址,并附带地址头                EndpointAddress clepaddr = new EndpointAddress(endpointAddress, hdclient);                // 3、创建binding,必须与服务器的定义一致                BasicHttpBinding clientbinding = new BasicHttpBinding(BasicHttpSecurityMode.None);                // 4、创建通道工厂                ChannelFactory
factory = new ChannelFactory
(clientbinding, clepaddr); // 创建通道,由于内部已实现,可以用协定直接作为通道对象 IDemo channel = factory.CreateChannel(); // 5、调用服务 string response = channel.Hello("Jack"); Console.WriteLine("调用结果:{0}", response); factory.Close();

 

插入到消息在的地址头如下图所示。

 

由于服务器在定义终结点地址时插入了地址头,所以,在客户端调用时,终结点地址和附带的地址头必须要与服务器所指定的一致,否则会调用失败。

比如,我们把上面客户端调用代码中的地址头信息改为“广州市”,然后再次调用服务。

AddressInfo cl_addrinfo = new AddressInfo();                cl_addrinfo.Province = "广东省";                cl_addrinfo.City = "广州市";

 

这时候调用服务,你会看到以下精彩一幕。

 

所以说啊,客户端在调用时,必须提供与服务器指定完全相同的地址头。这是默认的地址筛选方案导致的,默认情况下,两者的地址头必须完全一样才能通过筛选。

那么,有朋友一定会问,能不能让服务器和客户端所指定的地址头不一样,但又可以让服务器匹配筛选呢?关于这个问题嘛,老周会在下一篇烂文中给大伙伴们介绍。

 

-----------------------------------------------------------------

上面说了用代码的方式来指定地址头,那么,对应地,肯定得看看如何在配置文件中指定地址头了。

由于配置文件本身就是一个XML文件,所以,在配置文件中指定就更好办了,直接写XML节点就行了。

比如,服务器可以这样:

三国演义
2007-3-19
39.88

<headers>节点下可以添加任意XML节点,只要XML元素是完整的就行了。在处理SOAP消息时,是通过XML命名空间和元素名称来查找消息头的,所以,老周建议大家不要添加元素名和命名空间相同的多个头。比如这样

三国演义
2007-3-19
39.88
红岩
2003-10-5
26.5

当然,这样添加地址头,只要客户端调用时提供相同的地址头,是不会出错的,不过,如果你希望在代码中通过GetHeader<T>或FindHeader方法来获取消息头的话,要是找到元素名和命名空间相同的头,就会发生异常。这时候,你就只好使用最笨的方法来读取消息头了——读取普通XML文档的方式。

所以,为了减少处理麻烦,最好不要在地址头中放置相同结构的XML元素,当然了,如果你不打算在代码中读取消息头,而仅仅是为了加强客户端与服务器的地址验证,就无所谓了。

 

在客户端,配置文件中指定的地址头必须与服务器的一致。

三国演义
2007-3-19
39.88
红岩
2003-10-5
26.5

这样做是为了增强对终结点地址的验证。

有关如何自行定义地址头筛选方案,而不是使用默认的完全匹配方案,下次再聊,88。

 

示例代码下载:

 

转载地址:http://rialo.baihongyu.com/

你可能感兴趣的文章
网络安全领袖施奈尔:政府更多地参与网络安全不可避免
查看>>
OpenSSL将于9月22日发布多个漏洞补丁
查看>>
Win10失误推送Build 16212系统更新:用户悲剧!
查看>>
从Tesco银行账户被盗看金融企业安全应急措施
查看>>
集成商调研:中小型集成商的优势与劣势
查看>>
让Facebook死磕的对象 已成心腹大患?
查看>>
外媒关注中国通往5G之路:预计投入3000亿
查看>>
中国发布5G研发测试结果 关键技术已通过验证
查看>>
Qt Creator 运行s60 Emulator
查看>>
从供给侧改革看中国集成电路产业投资热
查看>>
测试之道--阿里巴巴八年测试专家倾情奉献
查看>>
大数据助推新型智库建设
查看>>
新加坡欲重组通信和媒体管制机构
查看>>
微信公众号最新数据解读,三分之一停更成僵尸号
查看>>
软件自动化测试成功公式
查看>>
少走弯路,中小企业OA选型攻略
查看>>
万能的小苏打,知道的人太少了,赶紧收藏
查看>>
从MWC 2017看懂英特尔5G朋友圈
查看>>
Facebook 的移动端 A/B 测试框架
查看>>
《交互式程序设计 第2版》一2.3.4 运算符
查看>>