EndpointAddress——不只是一个Uri[下篇]

上篇》对AddressHeader在服务端和客户端的作用,以及如何通过配置和编成的方式设置AddressHeader进行了详细介绍。现在我们通过一个实例来演示终结点的地址报头如何影响实现终结点选择的消息筛选机制。这个实例通过为服务端终结点指定地址报头实现针对客户端的授权,让经过许可的客户端才能访问这个服务。具体来说,我们将一个代码序列号的GUID作为终结点的地址报头。对于客户端发送的消息,只有具有相应的报头才能访问服务。[三个实例源代码下载地址:实例1实例2实例3]

一、无地址报头下服务调用(实例1

我们采用计算服务的例子,整个实例的解决方案具有右图所示的3个项目。其中类库项目Service.Interface用于定义契约接口。Service项目是一个控制台应用程序,用于定义服务类型和作为服务的宿主。控制台应用程序Client代码进行服务调用的客户端。在本书后续部分的绝大部分实例都会采用这个结构。

实例演示的目的旨在旨在指导读者编程,或者说明某个方面的原理,所以我会将服务承载的业务功能尽量地简化。所以我们分别在Service.Interface和Service项目中定义了如下所示的契约接口ICalculator和服务类型CalculatorService。ICalculator仅仅具有唯一的表示加法运算的Add操作。

ICalculator:

   1: using System.ServiceModel;
   2: namespace Artech.WcfServices.Service.Interface
   3: {
   4:     [ServiceContract(Name = "CalculatorService", Namespace ="http://www.artech.com/")]
   5:     public interface ICalculator
   6:     {
   7:         [OperationContract]
   8:         double Add(double x, double y);
   9:     }
  10: }

CalculatorService:

   1: using Artech.WcfServices.Service.Interface;
   2: namespace Artech.WcfServices.Service
   3: {
   4:     public class CalculatorService : ICalculator
   5:     {
   6:         public double Add(double x, double y)
   7:         {
   8:             return x + y;
   9:         }
  10:     }
  11: }

服务CalculatorService通过控制台程序Service进行寄宿。下面是服务寄宿代码和相应的配置。从配置可以看到,服务唯一的终结点具有一个作为地址报头的<sn>元素,它的值代表服务的序列号。

服务寄宿程序:

   1: using System;
   2: using System.ServiceModel;
   3: namespace Artech.WcfServices.Service
   4: {
   5:     class Program
   6:     {
   7:         static void Main(string[] args)
   8:         {
   9:             using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
  10:             {
  11:                 host.Open();
  12:                 Console.Read();
  13:             }
  14:         }
  15:     }
  16: }

配置:

   1: <configuration>
   2:     <system.serviceModel>
   3:         <services>
   4:             <service name="Artech.WcfServices.Service.CalculatorService">
   5:               <endpoint address="http://127.0.0.1:3721/calculatorservice"
   6:                         binding="ws2007HttpBinding"                        
   7:                         contract="Artech.WcfServices.Service.Interface.ICalculator">
   8:                 <headers>
   9:                   <sn xmlns="http://www.artech.com/">
  10:                        {DDA095DA-93CA-49EF-BE01-EF5B47179FD0}
  11:                      </sn>
  12:                 </headers>
  13:               </endpoint>
  14:             </service>
  15:         </services>
  16:     </system.serviceModel>
  17: </configuration>

客户端通过ChannelFactory<TChannel>创建的服务代理进行服务调用。下面是进行服务调用的程序和客户端配置。

服务调用程序:

   1: using System;
   2: using System.ServiceModel;
   3: using Artech.WcfServices.Service.Interface;
   4: namespace Artech.WcfServices.Client
   5: {
   6:     class Program
   7:     {
   8:         static void Main(string[] args)
   9:         {
  10:             using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
  11:             {
  12:                 ICalculator calculator = channelFactory.CreateChannel();
  13:                 Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2,calculator.Add(1,2));
  14:             }
  15:             Console.Read();
  16:         }
  17:     }
  18: }

配置:

   1: <configuration>
   2:   <system.serviceModel>
   3:     <client>
   4:       <endpoint name="calculatorservice"
   5:                address="http://127.0.0.1:3721/calculatorservice"
   6:                binding="ws2007HttpBinding"
   7:                contract="Artech.WcfServices.Service.Interface.ICalculator"/>
   8:       </client>
   9:   </system.serviceModel>
  10: </configuration>

由于进行服务调用的客户端终结点并没有一个相应的表示序列号的<sn>地址报头,在进行服务调用的时候没有显式地将序列号作为报头添加到请求消息中,所以针对服务端来说,这是一个不被许可的客户端。客户端运行后将会抛出如下图所示的EndpointNotFoundException异常。(S201)

二、为请求消息添加地址报头(实例2

假设服务端将作为序列化的GUID分发给经过许可的客户端,那么它就可以将其作为客户端终结点的地址报头定义到配置文件中,也可以在消息发送之前将序列化作为报头添加到请求消息中。第一种方式比较简单,我们来演示第二种方式。我们采用如下的代码进行服务调用,在调用之前将序列号作为报头添加到请求消息的报头列表中。在这种情况下,服务嗲用将会顺利进行。(S202)

   1: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
   2: {
   3:     ICalculator calculator = channelFactory.CreateChannel();
   4:      using (OperationContextScope contextScope = new OperationContextScope(calculator as IClientChannel))
   5:     {
   6:         string sn = "{DDA095DA-93CA-49EF-BE01-EF5B47179FD0}";
   7:         string ns = "http://www.artech.com/";
   8:         AddressHeader addressHeader = AddressHeader.CreateAddressHeader("sn", ns, sn);
   9:         MessageHeader messageHeader = addressHeader.ToMessageHeader();
  10:         OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
  11:         Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, calculator.Add(1, 2));
  12:     }
  13: }

输出结果:

   1: x + y = 3 when x = 1 and y = 2

之所以在请求消息中不存在于终结点地址报头相匹配的报头会导致抛出EndpointNotFoundException异常,原因在于按照默认的消息筛选机制找不到匹配的终结点。为了解决这个问题,对于客户端来说,可以通过在消息中添加相应的报头满足服务端筛选的条件;而对于服务端来说,则可以改变为了实现终结点的选择而采用消息筛选机制。总之一句话,只要服务端能够根据匹配的终结点就可以抑制EndpointNotFoundException异常的抛出。

三、改变地址筛选策略(实例3

我们可以在服务类型上应用ServiceBehaviorAttribute特性并为AddressFilterMode属性进行相应的设置来改变针对终结点地址的筛选机制。如下面的代码所示,AddressFilterMode属性是一个类型为AddressFilterMode的枚举。三个枚举项(Exact、Prefix和Any)分别代表三种地址匹配的策略,即精确匹配,基于前缀匹配和匹配任意地址。

   1: [AttributeUsage(AttributeTargets.Class)]
   2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
   3: {
   4:     [DefaultValue(0)]
   5:     public AddressFilterMode AddressFilterMode { get; set; }
   6: }
   7: public enum AddressFilterMode
   8: {
   9:     Exact,
  10:     Prefix,
  11:     Any
  12: }

其中Exact和Prefix都需要进行地址报头的匹配,而Any则不需要。从应用在AddressFilterMode的DefaultValueAttribute特性可以看出,该属性的默认值是Exact,所以在默认的情况下采用的是针对地址的精确匹配。那么如果我们在CalculatorService上应用ServiceBehaviorAttribute特性并将AddressFilterMode设置为Any,即使请求消息中不具有相关的报头,服务调用也会成功。(S203)

   1: [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
   2: public class CalculatorService : ICalculator
   3: {
   4:     //省略成员
   5: }

本例虽然名为通过“通过地址报头实现对客户端的授权”,其实在真正的应用中我们不会通过这样的方式对服务授权。因为终结点的地址报头是元数据的一部分,客户端在获取服务发布的元数据时会将地址报头一并获取。

EndpointAddress——不只是一个Uri[上篇]

EndpointAddress——不只是一个Uri[下篇]

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-11-05 17:28:35

EndpointAddress——不只是一个Uri[下篇]的相关文章

EndpointAddress——不只是一个Uri[上篇]

终结点是整个WCF的核心,由经典的ABC三要素组成.作为表示地址的EndpointAddress,很多人仅仅将其看成是一个表示标识服务并且表示服务所在地址的Uri,其实服务标识和定位服务仅仅是EndpointAddress一个基本的功能,它不仅仅是Uri那么简单. 一.EndpointAddress的三个功能 作为终结点的三要素之一的地址(Address),在基于WCF的通信中不仅仅定位着服务的位置,而且还提供额外的寻址信息.除此之外,终结点地址还和安全有关系,因为它包含着用于进行服务认证的服务

uri-反向排序一个 Uri 变量

问题描述 反向排序一个 Uri 变量 我想反向排序一个 Uri 变量. Uri 访问手机通话记录: Uri allCalls = Uri.parse("content://call_log/calls"); 当我反向排序时,它们还是从最原始的存储到最新的. 我使用: Collections.reverse 好像只能在数组中这么做,如何解决这个问题呢? 解决方案 当你使用 ContentResover 的查询功能时,可以输入排序选项. 查询(Uri uri, String[] proje

重新想象 Windows 8 Store Apps (33) - 关联启动: 使用外部程序打开一个文件或uri, 关联指定的文件类型或协议

原文:重新想象 Windows 8 Store Apps (33) - 关联启动: 使用外部程序打开一个文件或uri, 关联指定的文件类型或协议 [源码下载] 重新想象 Windows 8 Store Apps (33) - 关联启动: 使用外部程序打开一个文件或uri, 关联指定的文件类型或协议 作者:webabcd 介绍重新想象 Windows 8 Store Apps 之 关联启动 使用外部程序打开一个文件 使用外部程序打开一个 Uri 关联指定的文件类型(即用本程序打开指定类型的文件)

与众不同 windows phone (38) - 8.0 关联启动: 使用外部程序打开一个文件或URI, 关联指定的文件类型或协议

原文:与众不同 windows phone (38) - 8.0 关联启动: 使用外部程序打开一个文件或URI, 关联指定的文件类型或协议 [源码下载] 与众不同 windows phone (38) - 8.0 关联启动: 使用外部程序打开一个文件或URI, 关联指定的文件类型或协议 作者:webabcd 介绍与众不同 windows phone 8.0 之 关联启动 使用外部程序打开一个文件 使用外部程序打开一个 Uri 关联指定的文件类型 关联指定的协议 示例1.演示如何使用外部程序打开一

【Win10应用开发】协议-下篇:自定义多个协议

原文:[Win10应用开发]协议-下篇:自定义多个协议 前面介绍了如何为应用程序自定义协议,于是有朋友会问,我希望为我的应用注册多个协议,不同的协议处理不同的事情,能吗?答案是能的. 方法主要在配置清单文件上,这里我给出一个例子,示例应用将注册两个协议,分别为music:和video:. 在清单文件中找到Package/Applications/Application节点,在Application元素下增加Extensions节点,表示为应用程序声明的扩展,可以包含N个uap:Extension

Dojo学习笔记 9. dojo.graphics.color &amp;amp; dojo.uri.Uri

模块:dojo.graphics.color 下面是dojo里定义的颜色名称 dojo.graphics.color.named.white//白色 dojo.graphics.color.named.black//黑色 dojo.graphics.color.named.red//红色 dojo.graphics.color.named.green//绿色 dojo.graphics.color.named.blue//蓝色 dojo.graphics.color.named.navy//海军

WF4.0实战(十九):Silverlight+WCF+WF+Linq结合的一个示例

概述: 这个Demo主要是为了阐述WF4中是如何使用WCF服务的,以及如何在Silverlight中调用WCF服务.因为即使用了Silverlight呈现UI,又用Linq访问数据库.故本文的名字为:"Silverlight+WCF+WF+Linq结合的一个示例".如果你和我一样,对WCF有点了解,就知道WCF能将很多方法放在一Uri中供大家调用.那如何将多个WF流程放在一个Uri中供你调用呢?答案就是使用一个Pick活动,Pick活动中可以有多个分支,对于Pick的每一个分支,你都可

精通Grails: 用定制URI和codec优化Grails中的URI

在 "改变 Grails 应用程序的外观" 一文中,我们看到了如何使用层叠样式表(CSS)对一个 Grails 应用程序 - Blogito blog 站点 - 进行外观更改.这次,我将向您展示如何影响 Web 应用程 序的命脉: 用于导航的 URI.这对于像 Blogito 这样的 weblog 极其重要.指向单个条目的那些永久链 接(permalink)被像名片一样在 Internet 上传递:描述性越好,就越有效. 要获得描述性更好的 URI,需要定制控制器代码以支持个性化的 U

详解Android中的ContentProvider和Uri

一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共 享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过 ContentProvider对你应用中的数据进行添删改查.关于数据共享,以前我们学习过文件操作模式,知道通过 指定文件的操作模式为Context.MODE_WORLD_READABLE或Context.MODE_WORLD_WRITEABLE同样也可以对外共享 数