Skip to content

Soapite#

Soapite is the SOAP client: typed requests, typed responses and typed faults, through a client class generated from the WSDL. SOAP 1.1 and 1.2 are both supported.

The generated client#

ProRocket Lite makes one client unit per wsdl:service (ProRock.Soapite.<Service>), next to the units of its types. The client unit re-exports the request and response types, TSoapiteError and the exceptions, so for plain calls it is the only unit to use.

uses ProRock.Soapite.CheckVatService;

// pass the real endpoint: the default is the WSDL's soap:address, which may be outdated (VIES still declares http://)
var service := TCheckVatService.Create('https://ec.europa.eu/taxation_customs/vies/services/checkVatService');

Two forms of every operation#

Call On a SOAP Fault or a network failure
TryCheckVat(request, response, error) returns False, the reason in error: TSoapiteError; never raises for them
CheckVat(request) raises ESoapiteFault / ESoapiteTransport

The response is yours to free.

uses ProRock.Soapite, ProRock.Soapite.CheckVatService; // ProRock.Soapite: TSoapiteErrorKind

var response: TCheckVatResponseE;
var error: TSoapiteError;
if service.TryCheckVat(request, response, error) then
  try
    Writeln(response.Name, ' - valid: ', response.Valid);
  finally
    response.Free;
  end
else if error.Kind = sekFault then
  Writeln('the service refused: ', error.Code)
else
  Writeln('failed: ', error.Message);

TSoapiteError.Kind is one of sekNetwork, sekHttp, sekContent, sekFault. SOAP Faults of both versions are mapped to it (code, 1.2 subcode, reason, actor/node, detail), also when a service answers a fault with HTTP 200.

Server text

TSoapiteError.Message, Detail and Response are text controlled by the server and may contain its internals (stack traces, paths). Log them, and show your own message to end users.

Transport: timeouts, authentication, certificates#

The HTTP transport is THttpTransportNative from ProRock.Http, based on System.Net. HTTPS works out of the box (the operating system's TLS, no extra DLLs). Settings are made once on the transport and used for every call; credentials never appear in OnRequest / OnResponse or in error texts.

uses ProRock.Http; // THttpTransportNative

var transport := service.Transport as THttpTransportNative;
transport.ConnectionTimeout := 10000;          // ms
transport.ResponseTimeout := 30000;
transport.SetBasicAuth('user', 'password');   // HTTP Basic (use https)
transport.SetBearerToken(token);              // OAuth 2.0 bearer token
transport.SetHeader('X-API-Key', key);        // any header, e.g. an API key
transport.UserName := 'DOMAIN\user';          // or answer the server's challenge: Basic, Digest, NTLM, Negotiate
transport.Password := 'password';

Client certificates (mutual TLS), Delphi 12 or later:

transport.ClientCertificate := 'client.pfx';  // a PFX/P12 file (Android: the name of an installed certificate)
transport.ClientCertificateData := bytes;     // or the bytes of a PFX/P12 or PEM file, from memory (not on Android)
transport.ClientCertificatePassword := 'pfx password';

One of the two, not both. The sample ProRock.Http.ClientCertificate makes the call both ways.

A custom transport#

A different HTTP library, request signing, a fake for tests: a custom transport is a THttpTransport descendant that implements Send. The default headers (SetHeader, SetBasicAuth, ...) are already in the request when Send gets it. For example, a fake that answers every call with a prepared response:

uses ProRock.Http;

type
  TFakeTransport = class(THttpTransport)
  public
    LastRequest: THttpRequest;
    ResponseBody: string;
  protected
    function Send(const aRequest: THttpRequest): THttpResponse; override;
  end;

function TFakeTransport.Send(const aRequest: THttpRequest): THttpResponse;
begin
  LastRequest := aRequest;          // Method, Url, Headers, Body - the SOAP envelope
  Result.Status := 200;
  Result.Headers := [THttpHeader.Create('Content-Type', 'text/xml; charset=utf-8')];
  Result.Body := ResponseBody;      // a network failure: raise an exception here, as System.Net does
end;
var fake := TFakeTransport.Create;
fake.ResponseBody := TFile.ReadAllText('checkVatResponse.xml', TEncoding.UTF8);
service.Transport := fake; // the client owns its transport: the previous one is freed, this one is freed with the client

To change requests on their way out and keep System.Net (signing, for example), derive from THttpTransportNative instead:

function TSigningTransport.Send(const aRequest: THttpRequest): THttpResponse;
begin
  var request := aRequest; // a copy: aRequest is const
  request.AddHeader('X-Signature', Sign(request.Body));
  Result := inherited Send(request);
end;

SOAP headers#

Header blocks (WS-Addressing, WS-Security tokens, a service's own headers) are added in OnRequestHeader and read in OnResponseHeader. Header blocks are Xmlite objects, so generate them from their schema like any other type. The sample ProRock.Soapite.WsSecurity adds a WS-Security UsernameToken this way, from units generated from the OASIS schemas.

Logging#

OnRequest and OnResponse give the raw XML of every call - the envelope only, without the transport's credentials.