从SOAP请求中删除命名空间

时间:2010-11-11 12:56:00

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

我已导入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缺陷......

2 个答案:

答案 0 :(得分:14)

如果服务期望:

  <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

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

干杯,

布诺

答案 1 :(得分:8)

OMG!它花了很多咖啡和大量的睡眠堕落,但我设法解决了我的问题!它也很合理......首先我按预期导入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;

在我看来,这是一个黑客攻击。但它并不是一个非常脏的。这是一个尝试,捕获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');。然后结果变为<cal:Calculate xmlns:cal="http://example.webservice/">。但是,需要完成更多的事情:将xmlns:cal移动到xml标头。但就像现在一样,它对我有用。


另一个注意事项:对于WSDL,我使用了以下设置:'One Outparam is return','Unwind literal params','Generate destructors','Warning comments',' emit literal types ' ,'将字符串映射到widestring'。其他选项包括:'生成有关类型和接口的详细信息','使用HTTP绑定忽略端口类​​型','验证枚举成员','导入错误类型','导入标题类型','处理包含和导入的模式','生成class alias as class types','Allow Out parameters'和'Process nillable and optional elements'。 emit literal types 是实用的,因为它围绕我调用的单个方法生成一个类。遗憾的是,这也无济于事,尽管该类可以帮助您通过重写ObjectToSOAP()方法来修改信封中较高层的SOAP请求。
信封本身的创建在SOAPEnv单元中它在OPToSOAPDomConv单元中使用。不幸的是,我还没有找到一种简单的方法来访问信封本身来改变标题以添加这个额外的命名空间。然后,我可以使用我自己的版本覆盖TSOAPDomConv类,该版本添加了额外的命名空间。但是代码现在正在为我工​​作,正如我父亲告诉我的那样,当他学会我编程时:永远不要修复任何没有破坏的东西。