可靠的 XML Web Service (2)

web|xml

标头的作用
在查看代码之前,我们需要了解一下 SOAP 主题,即标头。SOAP 1.1 规范中谈论最少的内容之一就是 SOAP 标头。标头提供了一种扩展消息处理体系结构的简单方法。SOAP 1.1 规范中提到:标头在实现与消息主体没有特定关系的处理规则(例如验证和事务管理)时非常有用。对任何类型的消息来说,SOAP 标头都是以独立方式对可靠性信息进行编码的完美解决方案。规范中还概述了实施和处理这些标头的标准和规则。

下面我们来看看如何实现包含可靠性信息的 SOAP 标头。首先要为标头确定架构。这很重要,因为它正是最终用户在支持该标头的 Web 服务的 WSDL 文件中看到的实际效果。对于此实现方案,我直接将处理 API 映射到 SOAP 标头。

我来解释一下。在我的处理 API 中有一个名为 ReliabilityInfo 的类。对此类进行实例化时,它将在运行时变成动态对象。也就是说,您可以从可靠性的角度设置确定如何处理出站消息的属性。此对象还可以在运行时序列化为 SOAP 标头。下面是该类及其成员的一个快照。为了清楚起见,我删除了具体的实现和私有成员。

[XmlRootAttribute(ElementName="ReliableHeader", _
   Namespace="http://ericRP/ReliableHeader/2001/", IsNullable=false)]
   public class ReliabilityInfo : SoapHeader
   {      
      public string Destination{}
      public string ConversationId{}
      public int MessageId{}
      public MessageStatus Status{}
      public DateTime SendDate{}
      public DateTime ExpireDate{}
      public string AckURL{}
public enum MessageStatus{}

[XmlIgnore]
      public int MaxRetry{}
      [XmlIgnore]
      public string Text{}
   }

序列化为出站 SOAP 消息时,标头如下所示:

<soap:Header>
<ReliableHeader xmlns="http://ericRP/ReliableHeader/2001/">
<ConversationId>b9e029e1-af0f-42cb-83b0-7888f9e3ffc4</ConversationId>
<MessageId>1</MessageId>
<Status>新</Status>
<SendDate>2001-11-06T14:59:02.1021226-08:00</SendDate>
<ExpireDate>2001-11-06T18:59:02.1021226-08:00</ExpireDate>
<AckURL>http://localhost:8082/ericRPAck/POAck.asmx</AckURL>
</ReliableHeader>
</soap:Header>

这是通过 .NET 框架中两个非常重要的类实现的。基本 XML 序列化块使用 System.Xml.Serialization 名称空间类来进行处理。这两个类一个是 XmlRootAttribute 属性类,我使用它告诉序列化程序将文档碎片的根称为 ReliableHeader,并将它与名称空间相关联。此后,所有公共成员也同样会被序列化,除非您告诉序列化程序忽略该成员。我使用的另一个重要名称空间是 System.Web.Services.Protocols。具体地说,即 SoapHeader 类。从该类进行继承时,它会自动将序列化的 XML 加入到 SOAP 消息中。本文稍后将论述如何将标头加入消息。

这非常强大,因为我不仅可以使用类型明确的、编译好的对象作为标头的基础,而且在这些类的成员内部还有特定的实现。

综述
好啦,上面只是对可靠性进行了简单的论述,并阐述了我自己的规范。下面让我们看一看它的代码。

扩展 Web 服务客户端代理
第一步是重新设置现有的 .NET Web 服务客户端代理。记住,此协议可以在任何 SOAP 处理引擎中实现。我选择了 .NET 框架,因为它易于使用、基于标准且可扩展。

选取一个现有的 Web 服务客户端代理(例如 PurchaseOrderProxy),然后添加以下代码:

该类中必须有名为 ReliableHeader 的公共成员,并且其类型必须为 ericRP.ReliabilityInfo。此成员将在运行时通过调用客户端进行设置。稍后将使用此成员为出站消息提供标头信息,并提供在跟踪过程中应用了该标头的消息的状态信息。例如:
public class PurchaseOrderProxy :
      System.Web.Services.Protocols.SoapHttpClientProtocol
   {
      public ReliabilityInfo ReliableHeader;
      
      //为了清楚起见,此处省略了其他代码
   }

在 Web 服务客户端代理中调用的方法必须使用以下属性进行批注:
[SoapHeader("ReliableHeader", Required=true)]

在序列化过程中,此属性将在运行时把适当的标头值加入到出站 SOAP 消息中。此处的 SubmitMessage 方法使用 SoapHeader 属性进行标记。注意,ReliableHeader 是在步骤 1 中实现的成员,而且是必须的。也就是说,如果不在运行时设置此成员,将产生异常。例如:

[SoapHeader("ReliableHeader", Required=true)]
public void SubmitMessage(object message)
{
this.Invoke("SubmitMessage", new object[] {message});      
}
   
在 Web 服务客户端代理中调用的方法必须包含以下属性:
[ericRP.Client.RPClientTrace.TraceExtension()]

此属性表示该方法支持自定义 SOAP 扩展。在消息被序列化之前和之后、且在消息被发送至底层传输机制之前,这种 SOAP 扩展将在运行时被调用。我通常是在消息从客户端计算机发送出去之前,对它进行一些简单的跟踪和记录。稍后再查看此扩展的实现情况。例如:

[ericRP.Client.RPClientTrace.TraceExtension()]
[SoapHeaderAttribute("ReliableHeader", Required=true)]
public void SubmitMessage(object message)
{
this.Invoke("SubmitMessage", new object[] {message});      
}

该类必须从 ericRP.Client.RPClientTrace.IClientTrace 实现。这种接口实现方案提供了基本的跟踪功能,以检查调用程序是否支持特定的跟踪协议。在后面的跟踪功能中可以看到此代码。例如:
public class PurchaseOrderProxy :
   System.Web.Services.Protocols.SoapHttpClientProtocol,
   ericRP.Client.RPClientTrace.IClientTrace
{
public ReliabilityInfo ReliableHeader;
   }

最后,该类必须实现 IClientTrace.GetReliabilityInfo 函数,该函数是 IClientTrace 接口要求的唯一函数。这是一个简单的机制,客户端可以使用它在运行时将消息的状态信息发送到跟踪服务。例如:
public class PurchaseOrderProxy :
   System.Web.Services.Protocols.SoapHttpClientProtocol,
   ericRP.Client.RPClientTrace.IClientTrace
{
public ReliabilityInfo ReliableHeader;

ericRP.ReliabilityInfo ericRP.Client.RPClientTrace.IClientTrace.GetReliabilityInfo()
{      
return ReliableHeader;
}
   }

看起来代码可能很多,但实际上却很简单。事实上,如果时间再多一些,我可以创建从 SoapHttpClientProtocol 继承的新类并明确加以实现,但这种方式对于服务器端来说会更有趣。

扩展 Web 服务服务器存根
下一步,我们来扩展现有的 Web 服务服务器存根。选取一个现有的 .NET Web 服务类(例如 ProcessPurchaseOrder),然后添加以下代码:

该类中必须有一个类型为 ericRP.ReliabilityInfo 的公共成员 ReliableHeader。稍后将使用此成员为入站消息提供反序列化标头信息,并提供在跟踪过程中应用了该标头的消息的状态信息。例如:
public class ProcessPurchaseOrder :
      System.Web.Services.WebService
   {
      public ReliabilityInfo ReliableHeader;
   }

在 Web 服务存根中调用的方法必须使用以下属性进行批注:
[SoapHeader("ReliableHeader", Required=true)]

在反序列化过程中,此属性将在运行时对入站 SOAP 消息中适当的标头值进行反序列化。此处的 SubmitMessage 方法使用 SoapHeader 属性进行标记。例如:

[WebMethod]
[SoapHeader("ReliableHeader", Required=true)]
public void SubmitMessage(object message)
{
//为了清楚起见,此处省略了一些代码   
}

在 Web 服务存根中调用的方法必须使用以下属性进行批注:
[ericRP.Server.RPServerTrace.TraceExtension()]

此属性表示该方法支持自定义 SOAP 扩展。稍后再查看此扩展的实现情况。

注意:此扩展与客户端扩展不同。
例如:

[SoapHeader("ReliableHeader", Required=true)]
[ericRP.Server.RPServerTrace.TraceExtension()]
public void SubmitMessage(object message)
{
//为了清楚起见,此处省略了一些代码   
}

在 Web 服务存根中调用的方法必须使用以下属性进行批注:
[SoapDocumentMethod(OneWay=true)]

此属性表示被调用的函数不返回值。更具体地说,即一旦消息被反序列化,此属性就会强制 Web 服务向客户端返回 HTTP 202 响应。对于分离消息的最终处理来说,这不失为一个有效的机制,否则,客户端就不得不同步地等待服务返回响应。例如:

[WebMethod]
[SoapDocumentMethod(OneWay=true)]
[SoapHeader("ReliableHeader", Required=true)]
[ericRP.Server.RPServerTrace.TraceExtension()]
public void SubmitMessage(object message)
{
//为了清楚起见,此处省略了一些代码   
}

该类必须从 ericRP.Server.RPServerTrace.IServerTrace 实现。这种接口实现方案提供了基本的跟踪功能,以检查调用程序是否支持特定的跟踪协议。在后面的跟踪功能中可以看到此代码。例如:
public class ProcessPurchaseOrder :
System.Web.Services.WebService, ericRP.Server.RPServerTrace.IServerTrace
   {
      public ReliabilityInfo ReliableHeader;
   }

最后,该类必须实现 IServerTrace.GetReliabilityInfo 函数。这是 IServerTrace 接口要求的唯一函数。例如:
public class ProcessPurchaseOrder :
System.Web.Services.WebService, ericRP.Server.RPServerTrace.IServerTrace
   {
      public ReliabilityInfo ReliableHeader;

ericRP.ReliabilityInfo ericRP.Server.RPServerTrace.IServerTrace.GetReliabilityInfo()
      {   
         return ReliableHeader;
      }
   }

好啦,现有的 Web 服务客户端和服务器已准备就绪。下面我们来看看 SoapExtension 是如何工作的。

查看 RPClientTrance 和 RPServerTrace
为了实现可靠性处理,我决定使用 SoapExtension。SoapExtension 是一个可继承的基类,使用它可以跟踪 SOAP 消息的出站序列化和入站反序列化。正是在这个跟踪过程中,对消息进行记录并检查其状态。记得前面讲过,Web 服务客户端代理方法实现 [ericRP.Client.RPClientTrace.TraceExtension()]。当调用该方法时,此属性将在 SOAP 消息出站序列化时调用以下代码。

需要特别指出的主要函数是 ProcessMessage。发送出站消息时,ProcessMessage 将提供有关该消息序列化之前和之后的全部状态信息。这时,将检查谁在调用并将 Client 属性的类级别成员与当前消息分离。然后检查消息是否处于 AfterSerialize 状态。如果已经序列化,则可以在消息被发送至服务器之前进行记录。通过名为 ProcessOutgoingMessageText 的自定义函数,首先进行一些流交换以免破坏底层消息流。然后检查客户端是否支持 IClientTrace 接口。如果客户端支持该接口,则表明它们也支持可靠性协议。通过接口检查功能,可以调用 GetReliabilityInfo 以便将当前消息返回可应用于该消息的 ConversationManager,然后设置一些属性并调用 LogMessage。LogMessage 将当前消息信息写入本地数据库,然后发送事件,通知客户端已记录该消息。

public class RPClientTrace : SoapExtension
{      
public override void ProcessMessage(SoapMessage message)
{
  SoapClientMessage tmpMsg = (SoapClientMessage)message;
  _client = tmpMsg.Client;

  if (message.Stage == SoapMessageStage.AfterSerialize)
  {
    ProcessOutgoingMessageText(message);
  }
}

  public void ProcessOutgoingMessageText(SoapMessage message)
  {
   newStream.Position = 0;
   TextReader reader = new StreamReader(newStream);
   StringBuilder strMessage = new StringBuilder();
   strMessage.Append(reader.ReadToEnd());
   newStream.Position = 0;
   Copy(newStream, oldStream);

   if(_client is Client.RPClientTrace.IClientTrace)
   {
    try
    {
Client.RPClientTrace.IClientTrace _ptrClient = _
   (Client.RPClientTrace.IClientTrace)_client;
   ReliabilityInfo rInfo = _ptrClient.GetReliabilityInfo();
      rInfo.Text = strMessage.ToString();
      rInfo.Destination = message.Url;
   rInfo.Manager.LogMessage(rInfo);
     }
     catch(Exception e)
     {
      throw e;
     }
    }
   }
}

服务器端的情况有点复杂,因为服务器需要为入站消息执行额外的操作。被调用的 Web 方法用于实现 [ericRP.Server.RPServerTrace.TraceExtension()]。以下代码将在入站消息反序列化之前和之后被调用:

public class RPServerTrace : SoapExtension
{
public override void ProcessMessage(SoapMessage message)
{
try
{
  switch (message.Stage)
  {
   case SoapMessageStage.BeforeDeserialize:
        ReadIncomingMessageText(message);
    break;
   case SoapMessageStage.AfterDeserialize:
     SoapServerMessage tmpMsg = (SoapServerMessage)message;
     _server = tmpMsg.Server;
     if(_server is Server.RPServerTrace.IServerTrace)
     {
          Server.RPServerTrace.IServerTrace _ptrServer = _
             (Server.RPServerTrace.IServerTrace)_server;
     ReliabilityInfo rInfo = _ptrServer.GetReliabilityInfo();
   ericRP.ReliabilityInfo tempInfo = (ericRP.ReliabilityInfo)message.Headers[0];
   tempInfo.Text = _tempMessage;
   Server.ConversationManager manager = new Server.ConversationManager();
   manager.ProcessMessage(tempInfo);

     }
   break;
   }
}

进行流交换和接口检查后,将在服务器对话管理器上调用 ProcessInboundMessage。ProcessInboundMessage 用于检查核心消息的状态。消息限制程序即在此使用。首先检查消息是否过期,然后检查其是否重复,最后检查其是否有序。如果满足所有三个条件,则将记录该消息并向客户端发送确认;如果不能满足任一条件,则将更改消息的状态并向客户端发送确认。

public void ProcessInboundMessage(ericRP.ReliabilityInfo rInfo)
{         
    try
    {
   if(IsExpired(rInfo))
   {
   rInfo.Status = ericRP.ReliabilityInfo.MessageStatus.Expired;
   SendAck(rInfo);
   }
   else if(IsDulplicate(rInfo))
   {
   rInfo.Status = ericRP.ReliabilityInfo.MessageStatus.Duplicate;
   SendAck(rInfo);
   }
   else if(IsNotOrdered(rInfo))
   {
   rInfo.Status = ericRP.ReliabilityInfo.MessageStatus.NotOrdered;
   SendAck(rInfo);
   }
   else
{
   rInfo.Status = ericRP.ReliabilityInfo.MessageStatus.Success;
   LogMessage(rInfo);
   SendAck(rInfo);
   }
    }
    catch(Exception e)
    {
   throw e;
    }
  }
}

要使本示例在生产环境中可行,还需要做大量的实施工作。更重要的是,API 应当基于一个以后将会发布的公共标准。本文的主要目的就是引发读者思考问题,了解一下 .NET 框架中的主要 Web 服务类,我想这两个目的都已经达到。如果您正在寻求可靠异步消息处理的成熟可用的实现方案,建议您看一看 Microsoft BizTalk Server 2000。

最后,您可以使用类似的跟踪功能执行所有类型的操作,例如加密、客户分配和通知。记住,这项附加的功能增加了 Web 服务所需的处理基础结构。

展望
无论是在规范还是在实现方面,XML Web Service 的未来都是光明的。Microsoft 将以协作的、标准驱动的方式工作,确保 XML Web Service 成为编写松散耦合的分散式应用程序的最佳体系结构。可靠的消息处理和事务规范将在以后发布。尽管还有大量的实现工作要做,但您现在就可以开始使用 SOAP 和 WSDL 构建您的框架。与公共规范进程越接近,以后的改动工作就越容易进行。SOAP 规范就是这种概念的一个显著例子。世界各地的开发人员都可以跟踪 SOAP 规范的发展进程,因此可以毫不费力地对自己的实现方案进行相应地调整。

希望我能有一个水晶球,为您准确地描绘未来 XML Web Service 的基础结构;希望我们目前依赖的所有公用服务,例如安全性、事务、存储、查询、路由、进程协调等等,将来都能够直接映射至 Web 服务体系结构;而且 GXA 实现方案可以渗透到核心开发语言、应用程序框架、业务处理框架和企业基础结构中。到那时,我们就能够真正地以无缝方式将任意两种服务耦合在一起。但愿 ericRP 不会真的成为服务背后的可靠性层。<微笑>

时间: 2025-01-19 16:03:01

可靠的 XML Web Service (2)的相关文章

可靠的 XML Web Service (1)

web|xml 可靠的 XML Web Service Eric SchmidtMicrosoft Corporation,XML Core Services 组,项目经理 2001 年 12 月 11 日 下载此专栏的示例代码. 注意:要下载与本文相关的代码,您需要:Visual Studio .NET Release Candidate(英文) SQL Server 2000(英文) 在 PDC 上,我谈论了有关可靠的 XML Web Service(Web 服务)的话题,这个话题源于过去一

教你学会XML Web Service 的基础

web|xml 什么是 XML Web Service? XML Web Service 是在 Internet 上进行分布式计算的基本构造块.开放的标准以及对用户和应用程序之间的通信和协作的关注产生了这样一种环境,在这种环境下,XML Web Service 成为应用程序集成的平台.应用程序是通过使用多个不同来源的 XML Web Service 构造而成的,这些服务相互协同工作,而不管它们位于何处或者如何实现. 有多少个构建 XML Web Service 的公司,就可能有多少种 XML W

构建安全的Xml Web Service系列(三)

web|xml|安全 首先介绍一下SSL, SSL 的英文全称是 "Secure Sockets Layer" ,中文名为 "安全套接层协议层 ",它是网景( Netscape )公司提出的基于 WEB 应用的安全协议.SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装.压缩.加密等基本功能的支持.SSL是Security Socket Layer的缩写,技术上称为安全套接

XML Web Service 基础 (转微软MSDN)

web|xml|微软 XML Web Service 基础 Roger WolterMicrosoft Corporation 2001 年 12 月 摘要:本文概述了 XML Web Service 对于开发人员的价值,同时还介绍了 SOAP.WSDL 和 UDDI. 目录 什么是 XML Web Service? SOAP WSDL UDDI 其他内容 什么是 XML Web Service?XML Web Service 是在 Internet 上进行分布式计算的基本构造块.开放的标准以及

构建安全的Xml Web Service系列之SSL篇

原文:构建安全的Xml Web Service系列之SSL篇   首先介绍一下SSL, SSL 的英文全称是 "Secure Sockets Layer" ,中文名为 "安全套接层协议层 ",它是网景( Netscape )公司提出的基于 WEB 应用的安全协议.SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装.压缩.加密等基本功能的支持.SSL是Security Soc

XML Web Service 安全性

web|xml|安全|安全性     当我们谈及 XML Web Service 时,人们最关心的问题就是其安全性. XML Web Service 安全吗? 鉴于安全性涉及诸多方面(例如身份验证和授权.数据隐私和完整性等),以及 SOAP 规范中根本没有提及安全性这一事实,我们不难理解人们为什么认为答案是否定的.但是,请不要低估了 Microsoft? XML Web Service.如今,您可以采取许多措施来创建安全的 XML Web Service. 要解决 XML Web Service

通过压缩SOAP改善XML Web service性能

web|xml|性能|压缩 压缩文本是一个可以减少文本内容尺寸达80%的过程.这意味着存储压缩的文本将会比存储没有压缩的文本少80%的空间.也意味着在网络上传输内容需要更少的时间,对于使用文本通信的客户端服务器应用程序来说,将会表现出更高的效率,例如XML Web services. 本文的主要目的就是寻找在客户端和服务器之间使交换的数据尺寸最小化的方法.一些有经验的开发者会使用高级的技术来优化通过网络特别是互联网传送的数据,这样的做法在许多分布式系统中都存在瓶颈.解决这个问题的一个方法是获取更

构建安全的Xml Web Service系列(一)

web|xml|安全 Xml Web Service 从诞生那天就说自己都么都么好,还津津乐道的说internet也会因此而进入一个新纪元,可5年多来,Xml Web Service并没有像当初宣扬的那样火起来,尽管在一些领域之内,也有人牛刀小试,但从整体而言,Service还并没有得到广泛的应用,原因有很多,有一些来源于目前各大厂商都坚持自己的service标准,不能形成统一,也有对现有的稳定系统不愿进行更改的原因,但还包括web service本身的原因,最明显的应该是两个:1) 安全,2)

构建安全的Xml Web Service系列(二)

web|xml|安全 上一篇文章地址:构建安全的Xml Web Service系列一之初探使用Soap头 要分析Xml Web Service的安全性,首先要解决的问题是我们能了解和清楚Soap消息的格式和内容,如果获得不了SoapMessage,分析如何能构建安全Xml web service也就无从下手,即使分析出来,自己也可能模模糊糊,不能定论.下面就分析下如何获得SoapMessage. 首先介绍一个类-SoapExtension,msdn对这个类的备注为:ASP.NET 允许通过扩展性