【问题标题】:How to Complete XML for SOAP Client如何为 SOAP 客户端完成 XML
【发布时间】:2012-01-27 20:26:19
【问题描述】:

我正在尝试实现一个 SOAP 客户端,以处理该位置的 WSDL 文件描述的 Web 服务的加利福尼亚工资税的电子报告:FSET Service。在这个 WSDL 文件中,有几个模式的引用,包括 http://schemas.xmlsoap.org/ws/2004/08/addressing 的一个模式,它似乎在我的程序到达 OnBeforeExecute 事件之前就停止了它。

为了启动这个项目,我尝试访问在 WSDL 文件中声明的 Ping 函数。

我刚刚获得了一些在 Delphi 中创建 SOAP 客户端的经验。我正在使用 Delphi 2010。这是我为达到这一点所做的。我使用 WSDL 导入器将文件导入到上述地址。它为我创建了一个名为 fsetservice 的单元。然后,我在我的项目中添加了一个表单,并在其中添加了一个 HTTPRIO 组件。我将组件的 WSDLLocation 设置为 WSDL 文件的本地副本,在表单上放置几个 TMemos 以捕获请求和响应标头并添加代码来执行此操作。然后我设置了一个按钮,在它的 OnClick 事件中,我写了这段代码:

procedure TForm1.Button1Click(Sender: TObject);
var sResponse: String;
begin
  sResponse := GetFsetServiceSoap.Ping;
end;

当我单击按钮时,我在到达 HTTPRIO OnBeforeExecute 事件中的断点之前收到此消息:

标题http://schemas.xmlsoap.org/ws/2004/08/addressing:Action 为 最终收件人是必需的,但邮件中不存在

我知道每次发帖我们应该只问一个问题,但有时你的知识不足以问一个好问题:

编辑:为了节省其他有相同问题的人的时间,我将我找到的答案放在这些问题下。

看起来 HTTPRIO 组件可能正在检查从 WSDL 文件导入的 XML 是否与 WSDL 中引用的模式的完整性。这看起来很酷,但这是真的吗?

*回答:不正确,至少根据页面底部http://www.tutorialspoint.com/wsdl/wsdl_definition.htm 的注释。根据注释,模式不必实际存在于该位置,它只需要唯一标识 WSDL 中使用的模式。*

在自动创建(且未修改)的 GetFsetService 函数中,我可以一直走到最后,错误发生在返回时,似乎不可能进入调用错误的实际过程。有没有我想念的方法?

回答:我仍然不确定,但似乎答案是否定的。无论如何,虽然从头开始写会比较乏味,但这比等待 WSDL 向导的奇迹更好。

我是否遗漏了一些说明我需要实现为我自动创建的 FsetService 接口的内容?

回答:显然不是。

我在 Marco Cantu 的《Mastering Delphi 2005》一书中看到,有一个由 Borland 提供的应用程序 (WebAppDbg.exe),它允许您查看在特定端口上发送的 HTTP。我试过但没有结果。这会有帮助吗?如果有,我应该使用哪个端口?

回答:使用 Fiddler2 工具。很好的发现。

如何解决这个错误?

回答:手工操作。

【问题讨论】:

  • 为什么将 WSDLLocation 属性指向本地文件?尝试将其设置为fsettestversion.edd.ca.gov/fsetproxy/fsetservice.asmx?WSDL
  • 您是否使用内置的 SOAP WSDL 生成器来生成您的 WSDL?显示您呼叫 Ping 的方式以及 Ping 呼叫的设置。
  • @WarrenP 他在消息中提到了 Delphi 2010...
  • @John - 好的。我刚刚选择了 File|New|Other|WSDLImporter 并从该 URL 导入以创建 fsetservice1.pas。然后,我从我的项目中删除了 fsetservice.pas 并重命名了新的来代替它。我将 HTTPRIO 组件中的 WSDLLLocation(不是 URL)更改为 edd WSDL URL。然后我又跑了一次。它产生相同的消息。
  • @WarrenP - 我没有尝试创建服务,所以我没有使用 WSDL 生成器。我对 ping 的调用是我上面在 button.OnClick 事件中显示的。我很困惑为什么这可以在我不必编写一个类来实现 fsetservice 接口的情况下工作,但我不记得在我所做的阅读中看到任何我应该这样做的内容。

标签: delphi


【解决方案1】:

首先,在处理肥皂和 Web 服务时,我会先将 Fiddler2 和 SoapUI 添加到您的工具库中。

听起来信封标题不完整。根据我使用 D2007 和 WCF Web 服务的经验,Delphi 无法正确构建信封头,因此我不得不拦截调用并添加如下项目:

procedure TMainForm.MyMessageEnvelopeComplete(Sender: TROSOAPMessage);
begin
  Sender.EnvelopeNode.AddAttribute('xmlns:a', 'http://www.w3.org/2005/08/addressing', False);
  Sender.HeaderNode.Add('a:Action').Value := 'http://Testservice.Connect/IConnect/Ping';
  Sender.HeaderNode.Add('a:To').Value := 'http://testservice-pc:2021/WSConnect';
end;

编辑:

如果你想手动构建soap信封和post命令,我已经使用以下代码完成了......

procedure TMainForm.Button5Click(Sender: TObject);
  procedure HandleError(const errorCode: integer);
  var
    errorMessage: AnsiString;
  begin
    SetLength(errorMessage, 256);
    FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_FROM_HMODULE,
                   Pointer(GetModuleHandle('wininet.dll')),
                   errorCode, 0, PChar(errorMessage), Length(errorMessage), nil);
    SetLength(errorMessage, StrLen(PChar(errorMessage)));
    raise Exception.Create(errorMessage);
  end;

  function BuildHeader: TStringStream;
  begin
    result := TStringStream.Create('');
    try
      result.WriteString('Content-Type: application/soap+xml;charset=UTF-8;action="http://Thermo.Connect/IHCSConnect/Ping"' + sLineBreak);
    except
      result.Free;
      raise;
    end;
  end;

  function BuildBody: TStringStream;
  begin
    result := TStringStream.Create('');
    with result do
      try
        WriteString('<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">' + sLineBreak);
        WriteString('<s:Header>' + sLineBreak);
        WriteString('<a:Action>http://Thermo.Connect/IHCSConnect/Ping</a:Action>' + sLineBreak);
        WriteString('<a:To>http://thermo-pc:2021/WSHCSConnect</a:To>' + sLineBreak);
        WriteString('</s:Header>' + sLineBreak);
        WriteString('<s:Body>' + sLineBreak);
        WriteString('<Ping xmlns="http://Thermo.Connect">' + sLineBreak);
        WriteString('</Ping>' + sLineBreak);
        WriteString('</s:Body>' + sLineBreak);
        WriteString('</s:Envelope>' + sLineBreak);
      except;
        result.Free;
        raise;
      end;
  end;

var
  InetRoot: HINTERNET;
  InetConnect: HINTERNET;
  Request: HINTERNET;
begin
  InetRoot := InternetOpen('GabeCode', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
    InetConnect := InternetConnect( InetRoot, 'thermo-pc:2021', 0, '',
                                    '', INTERNET_SERVICE_HTTP, 0, Cardinal(Self));
    try
      Request := HttpOpenRequest( InetConnect, 'POST', 'WSHCSConnect', 'HTTP/1.1', nil, nil,
                                  INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_NO_CACHE_WRITE,
                                  0);
      try
        // build add header items to the post request
        with BuildHeader do
        try
          HttpAddRequestHeaders(Request, PChar(DataString), Length(DataString), HTTP_ADDREQ_FLAG_ADD);
        finally
          Free;
        end;

        // build the body of data being posted and send the post
        with BuildBody do
        try
          if not HttpSendRequest(Request, nil, 0, PChar(DataString), Length(DataString)) then
            HandleError(GetLastError);
        finally
          Free;
        end;

      finally
        InternetCloseHandle(Request);
      end;
    finally
      InternetCloseHandle(InetConnect);
    end;
  finally
    InternetCloseHandle(InetRoot);
  end;
end;

【讨论】:

  • 这看起来很有帮助,但我认为 MyMessageEnvelopeComplete 是一个事件。这似乎不是 HTTPRIO 的事件。在 HTTPRIO 的一些子组件中,我忽略了一些事件。也许 HTTPWebNode.OnBeforePost 会有所帮助。我已经下载了 SOAPUI,并且正在学习 Fiddler2。谢谢指点。
  • 抱歉,在上面的示例中,我使用的是 Rem 对象... rem 对象控件能够根据您的需要在表单上放置不同的组件。上面的组件是 TROSOAPMessage。
  • 不需要道歉。 RemObjects 似乎是一个很棒的工具集,但我可能需要在没有它的情况下完成这项工作。我找到了一篇关于手动构建 SOAP 客户端的文章。这是一个有趣的前景。希望这不是职业。缺点是我必须学习 WSDL 向导和 HTTPRIO 的很多东西,但好处是,如果我能在这条道路上取得成功,我可能会了解很多事情。
  • 哇。非常感谢您发布您的代码。明天我会先试一试。我也很感谢您发布了发送消息的方法,因为这可能会节省我另一个小时的学习时间。我现在接受你的回答,我希望能够在你提供的帮助下取得重大进展。再次感谢。
猜你喜欢
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多