WCF后传系列(2):深入WCF寻址Part 2—自定义寻址报头

概述

WCF专题系列(1):深入WCF寻址Part1一文中,我们对Web服务寻址规范做了一些认识,了解了终结点引用和消息信息报头两种结构,该规范在Web服务中的地位举足轻重,后续我们会经常提到该规范。在本文中,我们将继续深入WCF寻址的内容,包括元数据中的终结点地址,自定义寻址标头等相关信息。

终结点地址定义

了解了Web服务寻址规范,再回到WCF,在WCF中,终结点地址是由EndpointAddress类来表示的,它其中很重要的几个部分是:一个表示服务地址的统一资源定位符 (URI),一个表示服务的安全标识的 Identity 和一个可选的 Headers 集合,其中Headers用于标识终结点或与终结点交互的更多详细寻址信息。如图1所示:

TerryLee_WCF_02  

图1

记的我在WCF专题系列(1):深入WCF寻址Part1一文提到过,每个终结点引用都可以包含一些添加额外标识信息的引用参数,即寻址标头,在 WCF 中,将这些引用参数建模为 AddressHeader 类的实例,这里的Headers属性就是这些实例的集合,可以通过AddressHeader类提供的静态方法CreateAddressHeader来创建一个AddressHeader实例,如下代码所示:

AddressHeader header = AddressHeader.CreateAddressHeader("basic",
                        "http://www.cnblogs.com/terrylee", "Terrylee");

指定终结点地址

在WCF中提供了基址技术,这使的我们在指定终结点地址时可以酌情选用相对地址或者绝对地址,指定绝对地址的方法是在终结点定义中提供完全限定的地址,如下代码所示:

<service name="TerryLee.WCFAddressing.Service.CalculatorService"
         behaviorConfiguration="calculatorBehavior">

  <endpoint address="http://localhost:8887/CalculatorService"
            binding ="basicHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
  </endpoint>
</service>

使用绝对地址固然简单,但是如果我们的服务需要公开多个终结点,而这些终结点地址又具有相同的基地址时,也许相对地址是更好的选择。在创建服务宿主对象时,提供一个基地址,如下代码所示:

using (ServiceHost calculatorServiceHost =
        new ServiceHost(typeof(CalculatorService),
        new Uri("http://localhost:8887/CalculatorService")))
{
    calculatorServiceHost.Opened += delegate
    {
        Console.WriteLine("Service begin to listen via the Address:{0}",
            calculatorServiceHost.BaseAddresses[0].ToString());
    };

    calculatorServiceHost.Open();
    Console.Read();
}

又或者同时在配置文件中指定基地址,这样就无须在每个终结点中指定绝对地址了,如下代码所示:

<service name="TerryLee.WCFAddressing.Service.CalculatorService"
         behaviorConfiguration="calculatorBehavior">
  <host>
    <baseAddresses>
      <add baseAddress="http://localhost:8887/Calculator"/>
    </baseAddresses>
  </host>
  <endpoint address="myservice1"
            binding ="basicHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
  </endpoint>
  <endpoint address="myservice2"
            binding ="wsHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
  </endpoint>
</service>

但请注意,基址技术是为我们在配置终结点时提供了方便,客户端对它是毫无所知的,客户端看到的仍然是绝对地址,在打开服务宿主时,它会匹配所有的相对地址,从而为每个终结点提供相应的绝对地址,如上面的示例,可以在WSDL中看到:

<wsdl:service name="CalculatorService">
  <wsdl:port name="BasicHttpBinding_ICalculator" binding="tns:BasicHttpBinding_ICalculator">
    <soap:address location="http://localhost:8887/Calculator/myservice1" />
  </wsdl:port>
  <wsdl:port name="WSHttpBinding_ICalculator" binding="tns:WSHttpBinding_ICalculator">
    <soap12:address location="http://localhost:8887/Calculator/myservice2" />
    <wsa10:EndpointReference>
      <wsa10:Address>http://localhost:8887/Calculator/myservice2</wsa10:Address>
      <Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
        <Upn>TerryLee-PC\TerryLee</Upn>
      </Identity>
    </wsa10:EndpointReference>
  </wsdl:port>
</wsdl:service>

如果在指定了基地址的情况下,有以下几种情况:指定相对地址为空,终结点地址与基地址相同;指定相对地址不为空,追加相对地址到基地址上;指定一个绝对地址,基地址不起作用,终结点地址仍然为指定的绝对地址;指定一个绝对地址和一个与基地址不同的绑定,基地址不起作用。现在有这样一段配置信息:

<service name="TerryLee.WCFAddressing.Service.CalculatorService"
         behaviorConfiguration="calculatorBehavior">
  <host>
    <baseAddresses>
      <add baseAddress="http://localhost:8887/Calculator"/>
    </baseAddresses>
  </host>
  <endpoint address=""
            binding ="wsHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
  </endpoint>
  <endpoint address="myservice2"
            binding ="wsHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
  </endpoint>
  <endpoint address="http://localhost:8886/CalculatorService"
            binding ="wsHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
  </endpoint>
  <endpoint address="net.tcp://localhost:8885/Calculator"
            binding ="netTcpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
  </endpoint>
</service>

可以在ServiceHost启动后,输出所有的终结点地址和绑定信息,如下代码:

ServiceDescription desc = calculatorServiceHost.Description;
foreach (ServiceEndpoint endpoint in desc.Endpoints)
{
    Console.WriteLine("Endpoint - address:  {0}", endpoint.Address);
    Console.WriteLine("           binding:  {0}", endpoint.Binding.Name);
    Console.WriteLine("           contract: {0}", endpoint.Contract.Name);
}

输出结果如图2所示:

TerryLee_WCF_05  

图2

元数据中终结点地址

终结点地址在WSDL中表示为对应终结点的 wsdl:port元素内的终结点引用(EndpointReference)元素。终结点引用包含终结点的地址以及所有的地址属性,如下示例代码所示:

<wsdl:service name="CalculatorService">
  <wsdl:port name="WSHttpBinding_ICalculator" binding="tns:WSHttpBinding_ICalculator">
    <soap12:address location="http://localhost:8887/Calculator" />
    <wsa10:EndpointReference>
      <wsa10:Address>http://localhost:8887/Calculator</wsa10:Address>
      <Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
        <Upn>TerryLee-PC\TerryLee</Upn>
      </Identity>
    </wsa10:EndpointReference>
  </wsdl:port>
</wsdl:service>

自定义寻址报头

在本文的终结点定义一节,我们提到了寻址报头,在某些情况下,我们可能希望通过自定义寻址报头来解决一些复杂的问题,如根据根据传入的寻址报头中是否包含某些信息,将其转发到不同的终结点,通过自定义寻址报头,可以实现SOAP消息的无限扩展,放置任何希望的控制信息到SOAP消息。如下面的代码:

using (ServiceHost calculatorServiceHost =
    new ServiceHost(typeof(CalculatorService),
    new Uri("http://localhost:8887/CalculatorService")))
{
    calculatorServiceHost.Opened += delegate
    {
        Console.WriteLine("Service begin to listen via the Address:{0}",
            calculatorServiceHost.BaseAddresses[0].ToString());
    };

    AddressHeader header =
        AddressHeader.CreateAddressHeader("basic",
        "http://www.cnblogs.com/terrylee", "Terrylee");

    EndpointAddress ea = new EndpointAddress(
        new Uri("http://localhost:8887/CalculatorService"), header);

    calculatorServiceHost.Description.Endpoints.Add(
        new ServiceEndpoint(
            ContractDescription.GetContract(typeof(ICalculator)),
            new WSHttpBinding(),
            ea));

    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
    behavior.HttpGetEnabled = true;
    calculatorServiceHost.Description.Behaviors.Add(behavior);

    calculatorServiceHost.Open();
    Console.Read();
}

我们在WSDL中可以看到该自定义的报头,它作为终结点引用的引用参数:

<wsdl:service name="CalculatorService">
  <wsdl:port name="WSHttpBinding_ICalculator" binding="tns:WSHttpBinding_ICalculator">
    <soap12:address location="http://localhost:8887/CalculatorService" />
    <wsa10:EndpointReference>
      <wsa10:Address>http://localhost:8887/CalculatorService</wsa10:Address>
      <wsa10:ReferenceParameters>
        <basic xmlns="http://www.cnblogs.com/terrylee">Terrylee</basic>
      </wsa10:ReferenceParameters>
      <Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
        <Upn>TerryLee-PC\TerryLee</Upn>
      </Identity>
    </wsa10:EndpointReference>
  </wsdl:port>
</wsdl:service>

截获到SOAP消息可以看到,在消息报头中添加了basic这样的信息,如下代码所示:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <basic xmlns="http://www.cnblogs.com/terrylee">Terrylee</basic>
    <To s:mustUnderstand="1">http://localhost:8887/CalculatorService</To>
    <Action s:mustUnderstand="1">http://tempuri.org/ICalculator/Add</Action>
  </s:Header>
  <s:Body>
    <Add xmlns="http://tempuri.org/">
      <x>1</x>
      <y>2</y>
    </Add>
  </s:Body>
</s:Envelope>

当然我们也可以通过配置的方式来自定义寻址报头,如下代码所示:

<service name="TerryLee.WCFAddressing.Service.CalculatorService"
         behaviorConfiguration="calculatorBehavior">
  <host>
    <baseAddresses>
      <add baseAddress="http://localhost:8887/Calculator"/>
    </baseAddresses>
  </host>
  <endpoint address=""
            binding ="wsHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
    <headers>
      <basic xmlns="http://www.cnblogs.com/terrylee">Terrylee</basic>
    </headers>
  </endpoint>
</service>

结束语

本文相对于WCF专题系列(1):深入WCF寻址Part1来说,注重于实际的使用,介绍了指定终结点地址、元数据中的终结点地址、自定义消寻址报头等,在下一篇中,我们将继续深入WCF寻址,探讨消息筛选器等问题。WCF寻址相关文章:

WCF专题系列(5):深入WCF寻址Part 5—逻辑地址和物理地址

WCF专题系列(4):深入WCF寻址Part 4—自定义消息筛选器

WCF专题系列(3):深入WCF寻址Part 3—消息过滤引擎

WCF专题系列(1):深入WCF寻址Part 1—Web服务寻址规范

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2008-10-26 15:52 TerryLee 阅读(4458) 评论(26)  编辑 收藏 网摘 所属分类: [04]  WCF后传

  回复  引用  查看    
#1楼2008-10-26 16:10 | Justin      
老张你太高产了,实在佩服!
  回复  引用  查看    
#2楼[楼主]2008-10-26 16:11 | TerryLee      
@Justin
貌似我不是老张,我是老李:-)

  回复  引用  查看    
#3楼2008-10-26 16:14 | 丁学      
确实,老张最近产量比较高,嘿嘿
  回复  引用  查看    
#4楼2008-10-26 16:14 | Justin      
@TerryLee
我晕,刚才看 jillzhang说你抢他饭碗来着,呵呵,结果顺手写成老张了!
WCF我还没专门看过呢,以后跟你俩混啦

  回复  引用  查看    
#5楼[楼主]2008-10-26 16:16 | TerryLee      
@丁学
再次强调,我是老李-_-

PS: 难道“张冠李戴”这个词是从这儿来的?

  回复  引用  查看    
#6楼[楼主]2008-10-26 16:19 | TerryLee      
@Justin
呵呵,没关系
大家一起学习,别这么客气:)

  回复  引用  查看    
#7楼2008-10-26 16:22 | mmic.net.cn      
老大真的很牛比,真的很配服
  回复  引用  查看    
#8楼2008-10-26 16:25 | 侯垒      
"张冠李戴"这个有意思.
先学习一下.

  回复  引用  查看    
#9楼[楼主]2008-10-26 16:26 | TerryLee      
@mmic.net.cn
呵呵,太客气了:-)

  回复  引用  查看    
#10楼[楼主]2008-10-26 16:33 | TerryLee      
@侯垒
这个词就是从这里产生的,呵呵

  回复  引用    
#11楼2008-10-26 16:46 | 阿斯顿飞[未注册用户]
敢问楼主:在WPF中,定义一个ValidationRule并通过Validation.AddErrorHandler()绑定事件,在该事件处理程序里面使用对话框提示错误,为什么有时候会弹出两次呢?
  回复  引用  查看    
#12楼2008-10-26 17:04 | 李永京      
老李,小李来啦,呵呵
  回复  引用    
#13楼2008-10-26 17:05 | 劳动时髦[未注册用户]
楼主插入代码的方式是通过什么插入的,怎么如此漂亮,我插入代码的时候怎么是乱的
  回复  引用  查看    
#14楼2008-10-26 17:53 | VisualStudio      
老大,你的代码是不是没有用后台的CODE,跟页面颜色搭配的好,嘿嘿!能不能说一下怎么弄得!因为我的页面跟你的一样!
  回复  引用  查看    
#15楼[楼主]2008-10-26 18:11 | TerryLee      
@阿斯顿飞
不好意思,WPF的问题我就无法帮您解决了:),你可以问问园子里的周银辉,他在这方面比较有研究:)

  回复  引用  查看    
#16楼[楼主]2008-10-26 18:11 | TerryLee      
@李永京
欢迎小李常来:)

  回复  引用  查看    
#17楼[楼主]2008-10-26 18:14 | TerryLee      
@劳动时髦
@VisualStudio
我用的是一个Windows Live Writer的插件,叫Paste from Visual Studio
http://11011.net/software/vspaste" target="_new">http://11011.net/software/vspaste

  回复  引用  查看    
#18楼2008-10-26 18:37 | jillzhang      
:),支持一个
  回复  引用  查看    
#19楼2008-10-26 18:38 | jillzhang      
诚邀老李加入WCF技术研究团队呀
http://www.cnblogs.com/jillzhang/archive/2008/04/17/1158166.html" target="_new">http://www.cnblogs.com/jillzhang/archive/2008/04/17/1158166.html
:),其实我好想直接给你加上。

  回复  引用  查看    
#20楼[楼主]2008-10-26 19:41 | TerryLee      
@jillzhang
加入,呵呵,其实下午就想找你加入的:)

  回复  引用  查看    
#21楼2008-10-26 19:42 | volnet(可以叫我大V)      
@李永京
小张,你也来啦~:)

老张(jillzhang),早上去看你的博客,里面个人形象上还是一个人,怎么现在多了一个~:)

还是比较喜欢这张!
http://www.cnblogs.com/images/cnblogs_com/volnet/WindowsLiveWriter/2008OctMVPOpenDay_C79E/treeylee-baojianqiang-anytao_thumb.gif" style="width:200px" />

  回复  引用  查看    
#22楼[楼主]2008-10-27 01:01 | TerryLee      
@volnet(可以叫我大V)
呵呵,小龚同学也来了。。。

  回复  引用  查看    
#23楼2008-11-03 17:29 | Fastyou      
如果在真实的项目中一般是写代码来确定endaddress,header还是会用配置文件。
  回复  引用  查看    
#24楼[楼主]2008-11-05 09:34 | TerryLee      
@Fastyou
放在配置文件中更好一些,但也不一定,呵呵

  回复  引用  查看    
#25楼2008-12-20 19:28 | xuesky      
正在学习WCF,呵,LZ真是强人,不愧为MPV
  回复  引用  查看    
#26楼2009-01-14 17:26 | 寻梦E.net      
请问李老师:
这里的相对地址myservice1是什么呀。一个类名?还是可以随便写的?

<endpoint address="myservice1"
binding ="basicHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator">
</endpoint>

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1319852




相关文章:

相关链接: