Jump to content
ertank

Delphi SOAP response is always nil

Recommended Posts

Hello,

 

I tried to find solution to my problem in several places already including stackoverflow 

 

When consuming a SOAP web service, response to a request comes in fine, but it is not populated in response object in code. I already provided details in above link. Let me know if they really need to be in this post, please.

 

I added all files needed to test my case. I did not provide username and password as they are irrelevant to my problem. Below is an example code to use provided units.


 

uses

  EFinans.EArsivFatura;



procedure TForm1.Button1Click(Sender: TObject);
var
  Input: TFaturaOlusturInput;
begin

  // required before calling any EFinans.EArsivFatura procedure/function 

  EFinans.EArsivFatura.URLEArsivFatura := 'https://earsiv.efinans.com.tr/earsiv/ws/EarsivWebService';
  EFinans.EArsivFatura.Username := 'someuser';
  EFinans.EArsivFatura.Password := 'somepassword';



  // preparing parameters

  Input.donenBelgeFormati := Ord(TGelenBelgeFormatlari.gePDF);
  Input.goruntuOlusturulsunMu := 1; // 0=hayır, 1=evet
  Input.islemId := TGUID.NewGuid.ToString().Substring(1, 36).ToLower();
  Input.vkn := '123456789';
  Input.sube := '000000';
  Input.kasa := '0000';
  Input.numaraVerilsinMi := 0;
  Input.faturaSeri := EmptyStr;
  Input.sablonAdi := 'einvoice_efinans_15_06_04_3.xslt';
  Input.erpKodu := 'ERP1';
  Input.gzip := 1;
  if not EFinans.EArsivFatura.AFaturaOlustur(Input, TGidenBelgeFormatlari.PDF, 'test.zip', 'test.pdf') then
  begin
    ShowMessage(EFinans.EArsivFatura.LastError);
    Exit();
  end;
end;

Above sample code will get you an error response from web service. That response will be saved in "response.xml" file in same directory as your EXE.

 

If you debug and put a break point in line 237 of EFinans.EArsivFatura.pas you should see that Response.return is nil. That is my problem. I have same problem in other methods, too. Just trying to see how I fix this single one now.

 

 

Thanks & regards,

Ertan

EarsivWebService1.pas

EFinans.EarsivFatura.Utils.pas

EFinans.EArsivFatura.pas

response.xml

test.zip

Edited by ertank
Put additional details

Share this post


Link to post

Please provide the details here as well. Thank you.

Sherlock added: Even though most users are members of several services, for highest impact you should post the complete question here as well. Perhaps the person that could really help you is not a StackOverflow member, and can't read your post there.

Share this post


Link to post

As I remarked in StackOverflow, that WSDL is quite complex and unusual. Have you created the SOAP service?

Share this post


Link to post

SOAP service side is most likely prepared in C#. Not my web service at all. This is an electronic invoice service provider. Company between government and developers.

Share this post


Link to post
Guest

I'm not sure this is helpful... I remember many years back I faced similar problems. I think I wanted to consume from Exchange Web Services. After ditching Delphi's own stuff I went Rem-objects. There was support and they tried but concluded that the schema was too advanced or Microsoft-y or maybe to C-ish. Don't remember. Anyhow, to the point;

 

I downloaded the Visual Studio free and wrote a small dll that I used in Delphi with AtoZ's CrossTalk all the array handling and advanced properties that no Delphi-native lib could handle was no problem whatsoever.

 

IMHO once you solve this problem, the next pops up. My recommendation is talk to C# soap/wdsl with C# and so on. It's simply to ORMish to translate well.

 

HTH, (flame suit on)...

 

/D (phone)

Edited by Guest

Share this post


Link to post
On 10/26/2018 at 11:12 AM, ertank said:

SOAP service side is most likely prepared in C#. Not my web service at all. This is an electronic invoice service provider. Company between government and developers.

Hi,

 

This SOAP web service can't be imported in Delphi via WSDL importer 😞 Without import you can't use it directly If you don't understand all objects ... Wsdl importer error message "XML document must have a top level element" Line 0.

Wsdl is generated with  "Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.5-b05 . " and not 

 

yours service

<!--
Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.5-b05 .
-->
<!--
Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.5-b05 .
-->
<binding xmlns:ns1="http://service.earsiv.uut.cs.com.tr/" name="EarsivWebServicePortBinding" type="ns1:EarsivWebService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
....

 

Example from c# .Net framework

<wsdl:definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="????????" xmlns:s1="http://microsoft.com/wsdl/types/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="????????">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://ros.si/R16">
<s:import namespace="http://microsoft.com/wsdl/types/"/>

 

My guess is you have also REST available !?

 

Solution : c# import that Service and wrap it to helper service (regular SOAP service) ... and import wrapped WSDL into Delphi

 

 

regards,

M

Edited by mausmb

Share this post


Link to post
5 hours ago, mausmb said:

My guess is you have also REST available !?

 

Solution : c# import that Service and wrap it to helper service (regular SOAP service) ... and import wrapped WSDL into Delphi

Unfortunately, I do not have REST version of the same web service. Only SOAP. Probably government enforcement.

 

I will see if I can find anyone familiar with C# and web services. I do not have enough knowledge of C# to import and then wrap for importing WSDL from Delphi.

 

Thanks.

Ertan

Share this post


Link to post

Without hacking me into the sources or reading through the thread, did you format the imported wsdl unit in the IDE? Ctrl-D, or 3rd party?

Because this wrapper unit is case sensitive and formatting can corrupt it.

Edited by Attila Kovacs
  • Thanks 1

Share this post


Link to post
23 minutes ago, Attila Kovacs said:

Without hacking me into the sources or reading through the thread, did you format the imported wsdl unit in the IDE? Ctrl-D, or 3rd party?

Because this wrapper unit is case sensitive and formatting can corrupt it.

I did not. I used WSDLImp.exe and unit is what it imported. Code is not formatted.

Share this post


Link to post
11 hours ago, Attila Kovacs said:

Without hacking me into the sources or reading through the thread, did you format the imported wsdl unit in the IDE? Ctrl-D, or 3rd party?

Because this wrapper unit is case sensitive and formatting can corrupt it.

Wow! That is good to know. Did not know that. Thanks!

Share this post


Link to post

My knowledge, Delphi does not support namespaces in web services. I now believe, response cannot be parsed by Delphi because both request and response contain "namespaces".

 

I sincerely hope both namespace and easy security header support gets introduced in following versions of Delphi.

 

For now, I give up searching for a solution. I will be generating my own request XML by code, and I will be parsing incoming response XML by code using THTTPRIO.OnBeforeExecute() and THTTPRIO.OnAfterExecute() events.

 

Thanks to everyone.

 

Regards,

Ertan

Share this post


Link to post

Hi @ertank!  I play with your code and do some debugging in the depths of the Delphi SOAP sources. As you can see in the TOPToSoapDomConvert.ProcessResponse (Line 2037 in Tokyo 10.2.3) it calls a inline class helper function IsBareLiteral. 

 

Making long story short: Set every time the THTTPRIO.Converter.Options (see Unit Soap.OpConvertOptions) and the Response will be filled.

 

Try this:

 

unit Earsiv.View;

interface

uses
  System.SysUtils,
  System.Classes,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  EarsivWebService1,
  Soap.SOAPHTTPClient,
  Soap.OpConvertOptions;

type
  TForm5 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FRIO: THTTPRIO;
    WS: EarsivWebService;
    procedure MyHTTPRIO1AfterExecute(const MethodName: string; SOAPResponse: TStream);
  public
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}


procedure TForm5.Button1Click(Sender: TObject);
var
  Request: faturaOlustur;
  Response: faturaOlusturResponse;
begin
  if not Assigned(FRIO) then
  begin
    FRIO := THTTPRIO.Create(nil);
    FRIO.OnAfterExecute := MyHTTPRIO1AfterExecute;
    FRIO.URL := 'https://earsiv.efinans.com.tr/earsiv/ws/EarsivWebService';
    // the following line are not enough, see MyHTTPRIO1AfterExecute
    FRIO.Converter.Options := FRIO.Converter.Options + [soDocument, soLiteralParams];
    WS := (FRIO as EarsivWebService);
  end;

  Request := nil;
  Response := nil;

  Request := faturaOlustur.Create();
  Request.input := 'Hello';
  Request.fatura := belge.Create();
  Request.fatura.belgeFormati := belgeFormatiEnum.PDF;

  try
    Response := WS.faturaOlustur(Request);
  finally
    if Assigned(Response) and Assigned(Response.return) then
    begin
      ShowMessage(Response.return.resultCode + sLineBreak + Response.return.resultText);
    end;

    Request.Free;
    Response.Free;
  end;
end;

procedure TForm5.MyHTTPRIO1AfterExecute(const MethodName: string; SOAPResponse: TStream);
begin
  FRIO.Converter.Options := FRIO.Converter.Options + [soDocument, soLiteralParams];
end;

end.

 

Edited by TiGü
  • Like 1

Share this post


Link to post

Hm, after the first request, the Converter.Options are fine and included soDocument and soLiteralParams...so there is room for improvement (calling MyHTTPRIO1AfterExecute only once and disconnect it afterwards from FRIO.OnAfterExecute or something). 

Share this post


Link to post

Wow, it could be easier.
You have to set the Converter.Options after casting to the EarsivWebService interface. Then they are not reset and you can avoid the AfterExecute handler.

 

unit Earsiv.View;

interface

uses
  System.SysUtils,
  System.Classes,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  EarsivWebService1,
  Soap.SOAPHTTPClient,
  Soap.OpConvertOptions;

type
  TForm5 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    FRIO: THTTPRIO;
    FEarsivWebService: EarsivWebService;
  public
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

procedure TForm5.FormCreate(Sender: TObject);
begin
  FRIO := THTTPRIO.Create(nil);
  FRIO.URL := 'https://earsiv.efinans.com.tr/earsiv/ws/EarsivWebService';
  FEarsivWebService := (FRIO as EarsivWebService);

  FRIO.Converter.Options := FRIO.Converter.Options + [soDocument, soLiteralParams];
end;

procedure TForm5.Button1Click(Sender: TObject);
var
  Request: faturaOlustur;
  Response: faturaOlusturResponse;
begin
  Request := nil;
  Response := nil;

  Request := faturaOlustur.Create();
  Request.input := 'Hello';
  Request.fatura := belge.Create();
  Request.fatura.belgeFormati := belgeFormatiEnum.PDF;

  try
    Response := FEarsivWebService.faturaOlustur(Request);
  finally
    if Assigned(Response) then
    begin
      if Assigned(Response.return) then
      begin
        ShowMessage(Response.return.resultCode + sLineBreak + Response.return.resultText);
      end;
      Response.Free;
    end;

    Request.Free;
  end;
end;

end.

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×