如何为SOAP客户端完成XML

时间:2012-01-27 20:26:19

标签: delphi

我正在尝试实现一个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工具。很好找。

如何解决此错误?

答案:手工完成。

1 个答案:

答案 0 :(得分:4)

首先,我会在处理肥皂和网络服务时,将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;