【问题标题】:Removing the namespace from SOAP request从 SOAP 请求中删除命名空间
【发布时间】:2010-11-11 12:56:00
【问题描述】:

我已经导入了一个 WSDL 并使用它来发送一个 SOAP 请求。它看起来像这样:

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <SOAP-ENV:Body>
        <Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
            <ContractdocumentIn>
                <AL>
                ...More XML...

问题在于Calculate 元素中的xmlns="urn:xx.WSDL.xxxxxWebService" 部分。 Web 服务无法接受这一点。 Web 服务不喜欢这样的命名空间......
使用SoapUI 我发现这个请求可以正常工作:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:col="http://example.com.service.xxx/">
    <SOAP-ENV:Body>
        <col:Calculate>
            <ContractdocumentIn>
                <AL>
                    ...More XML...

那么,如何将请求从第一个版本更改为第二个版本? (不使用肮脏的技巧!)
(如果这会导致正确的请求格式,则重新导入不是问题。)




再次重申:不允许使用肮脏的伎俩,例如破解请求流来修改它!


虽然我还没有完全测试它,但 C#/VS2010 和 Delphi 2010 似乎也无法使用我试图调用的 Web 服务。似乎是用 Java 编写的 Web 服务。 SoapUI 恰好是用 Java 编写的,因此我们有一个 Java 客户端与一个 Java 服务通信,这似乎工作得很好。但是还有其他客户吗?
无论如何,是时候再添加两个标签了:“Java”,因为它是 Java 服务,以及“vs2010”,因为 .NET 也不喜欢这个服务。
我正准备在 .NET 中围绕这个服务编写一个包装器,希望那会工作......它不会。所以这是一个非常严重的缺陷,可能是Java缺陷……

【问题讨论】:

  • 祝你好运。我不得不诉诸肮脏的伎俩。
  • 是的,我知道。我可能可以通过使用肮脏的技巧来解决它,但管理层不同意这一点。所以代码需要干净。
  • SOAP 代码为您提供了钩子来检查正在发送的 XML,并在需要时对其进行修改。它显式地支持手动操作。并且代码将在那里 - 关于构建后操作没有什么“肮脏”的。可能不如自动的好,但没有人应该担心它。
  • 我知道。甚至不会太难。捕获 BeforeExecute 事件,读取 XML,使用样式表对其进行转换,然后使用新的 XML 重新创建流。这就是我向管理层提出的建议。他们确信这太复杂了,现在我需要找到其他解决方案。如果没有,他们将不得不接受这一点,尽管......
  • 如果没有好的答案出现,我会筹码获得赏金。

标签: java delphi visual-studio-2010 soap delphi-2007


【解决方案1】:

如果服务期望:

  <col:Calculate>
     <ContractdocumentIn>
         <AL>

Delphi SOAP 正在发送...

    <Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
        <ContractdocumentIn>
            <AL>

...问题在于 ContractdocumentIn 是一个不合格的元素,并且(直到 Delphi XE)Delphi SOAP 不支持作为操作的顶级元素的不合格元素。顶级元素是函数的参数,没有地方可以存储底层元素必须是不合格的事实;对于映射到属性的元素,我们使用属性的索引来存储 IS_UNQL 标志。

顺便说一句,没有必要使用前缀。该服务将(应该)也接受:

    <Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
        <ContractdocumentIn xmlns="">
            <AL>

后者更冗长但它相当于前缀大小写。

在 Delphi XE 中,导入器存储特定参数映射到不合格元素的事实,并且运行时会根据此信息进行操作。最近在一个线程中出现时,我在新闻组中发布了基于 D2010 和 D2007 的 XE 实现的补丁:

https://forums.embarcadero.com/thread.jspa?threadID=43057

如果有人需要访问它们(他们在附件区域,但可能已经滚掉了),请给我发电子邮件,我会让它们可用。 [在 embarcadero dot com 的 bbabet]

干杯,

布鲁诺

【讨论】:

  • 很好的解释!谢谢。我将 D2005 与 D2007 补丁一起用于 SOAP 库。你认为你对 D2007 补丁的修复对我有用吗?
  • 是的,应该。我贴的补丁在这里:forums.embarcadero.com/thread.jspa?messageID=290788
  • 如果您遇到问题,请指出(或通过电子邮件发送给我)WSDL,我会进行调查。干杯!
  • 很好的解释。实际上,我调用的服务更喜欢为 'col:' 提供​​一个额外的命名空间,如下所示:&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:col="http://example.com.service/"&gt;&lt;SOAP-ENV:Body&gt;&lt;col:Calculate&gt;&lt;ContractdocumentIn&gt;,但幸运的是,它也接受 &lt;Calculate xmlns="http://colan.ogconnect.service.wzp/"&gt;&lt;ContractdocumentIn xmlns=""&gt;
  • 这种方法看起来很不错,我们现在正在试验看看对我们现有的一些服务的影响。即“我们可以摆脱多少丑陋的 StringReplace 操作?”我们将在下周报告我们的结论。
【解决方案2】:

天哪!花了很多咖啡和大量的睡眠破坏,但我设法解决了我的问题!这也很简单......
首先,我按预期导入 WSDL。这将生成几个 TRemotable 类。然后,对于每个需要不同命名空间的 TRemotable,我重写了 ObjectToSOAP() 方法! (并将 XMLIntf 包含到 WSDL 源代码中。)在我的例子中,对于几种远程类型的代码如下:

function AL2.ObjectToSOAP( RootNode, ParentNode: IXMLNode; const ObjConverter: IObjConverter; const NodeName, NodeNamespace, ChildNamespace: InvString; ObjConvOpts: TObjectConvertOptions; out RefID: InvString ): IXMLNode;
begin
  Result := inherited ObjectToSOAP( RootNode, ParentNode, ObjConverter, NodeName, '', '', ObjConvOpts, RefID );
end;

这在 Delphi XE 中工作。在 Delphi 2007 中,我不得不在输入类型上使用单元 XMLIntf 和 XMLDoc 以及以下代码:

function ContractdocumentInType.ObjectToSOAP(RootNode, ParentNode: IXMLNode; const ObjConverter: IObjConverter; const Name, URI: InvString; ObjConvOpts: TObjectConvertOptions; out RefID: InvString): IXMLNode;

  procedure AlterChildren(Child: IXMLNode);
  var
    I: Integer;
  begin
    if (Child.NodeType = ntElement) then Child.SetAttributeNS('xmlns', '', '');
    for I := 0 to Pred(Child.ChildNodes.Count) do
      AlterChildren(Child.ChildNodes[I]);
  end;

begin
  Result := inherited ObjectToSOAP(RootNode, ParentNode, ObjConverter, Name, '', ObjConvOpts, RefID);
  AlterChildren(Result);
end;

在我看来,这是一个 hack。但这不是一个很脏的。这是一些试验,捕获 SOAP 请求和响应以检查它们的内容并查看它是否使用了正确的名称空间。不幸的是,Delphi XE 在这方面做得比 Delphi 2007 好得多。

尽管如此,我仍保留此 Q 以寻求更好的解决方案...


顺便说一句,要将 col: 添加到输出中,我还必须将 WSDL RemClassRegistry.RegisterXSClass(Calculate, 'http://colan.ogconnect.service.wzp/', 'Calculate'); 中的这一行更改为:RemClassRegistry.RegisterXSClass(Calculate, 'http://colan.ogconnect.service.wzp/', 'cal:Calculate');。然后结果变为&lt;cal:Calculate xmlns:cal="http://example.webservice/"&gt;。不过,还需要做更多的事情:将 xmlns:cal 移动到 xml 标头。但就目前而言,它对我有用。


另一个注意事项:对于 WSDL,我使用了以下设置:'One Outparam is return'、'Unwind literal params'、'Generate destructors'、'Warning cmets'、'emit literal types' , '将字符串映射到宽字符串'。其他选项包括:“生成有关类型和接口的详细信息”、“使用 HTTP 绑定忽略端口类​​型”、“验证枚举成员”、“导入错误类型”、“导入标头类型”、“进程包含和导入的模式”、“生成类别名作为类类型”、“允许输出参数”和“处理 nillable 和可选元素”。 emit literal types 很实用,因为它围绕我正在调用的单个方法生成一个类。不幸的是,这也无济于事,尽管该类将帮助您通过覆盖 ObjectToSOAP() 方法来修改信封内上层的 SOAP 请求。
信封本身的创建在 SOAPEnv 单元中,并且它在 OPToSOAPDomConv 单元中使用。不幸的是,我还没有找到一种简单的方法来访问信封本身来更改标题以添加这个额外的命名空间。再说一次,我可以用我自己的版本覆盖 TSOAPDomConv 类,它确实添加了额外的命名空间。但是代码现在对我有用,正如我父亲在学会我编程时告诉我的:永远不要修复任何没有损坏的东西。

【讨论】:

  • 看起来很有希望 - 我要试一试。这可能会为即将到来的大项目节省大量时间/痛苦,所以我很高兴在讨论中贡献赏金。
  • 它真的给你这样的输出吗:
  • 我认为这可能会给我一个很好的机会,但我可能需要在 AlterChildren 函数中做更多的工作。
  • col:calculate 是通过第二个技巧完成的。请参阅扩展答案。
  • 我要创建一个新类:TRemotableFix,它会覆盖 ObjectToSOAP。然后我可以简单地修改生成的代理以将所有/大多数 TRemotable 更改为 TRemotableFix,为我的新中间类添加 Uses,然后对 RegisterXSClass 进行编辑。我想要一个可以快速应用于任何代理类的“食谱”解决方案,因为我需要能够在以后重新使用 WSDL 而不会破坏一切。即对代理类的更改需要简短而甜蜜,并且可以通过 svn 历史记录轻松发现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多