Search code examples
web-servicesdelphisoapwsdlwebservice-client

Delphi SOAP without WSDL: why is my function result nil


I try to implement a soap client, that should call a service like this:

Request-XML:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope 
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <soapenv:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <wsse:BinarySecurityToken EncodingType="wsse:Base64Binary" ValueType="bipro:VDGTicket">ABCDEFG</wsse:BinarySecurityToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <RequestSecurityToken xmlns="http://schemas.xmlsoap.org/ws/2005/02/trust">
            <TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</TokenType>
            <RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</RequestType>
        </RequestSecurityToken>
    </soapenv:Body>
</soapenv:Envelope>

Response:

<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <wst:RequestSecurityTokenResponse xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
            <wst:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</wst:TokenType>
            <wst:Lifetime>
                <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-03-22T13:11:53.053Z</wsu:Created>
                <wsu:Expires xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-03-22T17:11:53.053Z</wsu:Expires>
            </wst:Lifetime>
            <wst:RequestedSecurityToken>
                <wsc:SecurityContextToken xmlns:wsc="http://schemas.xmlsoap.org/ws/2005/02/sc">
                    <wsc:Identifier>bipro:1438029780641932842</wsc:Identifier>
                </wsc:SecurityContextToken>
            </wst:RequestedSecurityToken>
            <allgemein:BiPROVersion xmlns:allgemein="http://www.bipro.net/namespace/allgemein">2.1.0.1.0</allgemein:BiPROVersion>
        </wst:RequestSecurityTokenResponse>
    </soapenv:Body>
</soapenv:Envelope>

Yes, there is also a WSDL file, but the code generator of Delphi does not generate code for everything in it... The generated code is missing two relevant type definitions... and there is no possibility to get a correct wsdl... so I decided to code the interface-unit on my own:

      BinarySecurityToken = class(TRemotable)
      private
        FEncodingType: string;
        FValueType: string;
        FVDGTicket: string;

      public
      published
        property EncodingType: string Index (IS_ATTR) read FEncodingType write FEncodingType;
        property ValueType: string Index (IS_ATTR) read FValueType write FValueType;
        property VDGTicket: string Index (IS_TEXT) read FVDGTicket write FVDGTicket;
      end;

      Security = class(TSoapHeader)
      private
        FBinarySecurityToken: BinarySecurityToken;
      public
        destructor Destroy; override;
      published
        property BinarySecurityToken: BinarySecurityToken Index (IS_REF) read FBinarySecurityToken write FBinarySecurityToken;
      end;

      TokenType = class(TRemotable)
      private
        FTokenType: string;
      public
      published
        property TokenType: string Index (IS_TEXT) read FTokenType write FTokenType;
      end;

      RequestType = class(TRemotable)
      private
        FRequestType: string;
      published
        property RequestType: string Index (IS_TEXT) read FRequestType write FRequestType;
      end;

      RequestSecurityToken = class(TRemotable)
      private
        FTokenType: TokenType;
        FRequestType: RequestType;
      public
        destructor Destroy; override;
      published
        property TokenType: TokenType Index (IS_REF) read FTokenType write FTokenType;
        property RequestType: RequestType Index (IS_REF) read FRequestType write FRequestType;
      end;

      Created = class(TRemotable)
      private
        FDate: TXSDateTime;
      public
        destructor Destroy; override;
      published
        property Date: TXSDateTime Index (IS_TEXT) read FDate write FDate;
      end;

      Expires = class(TRemotable)
      private
        FDate: TXSDateTime;
      public
        destructor Destroy; override;
      published
        property Date: TXSDateTime Index (IS_TEXT) read FDate write FDate;
      end;

      Lifetime = class(TRemotable)
      private
        FCreated: Created;
        FExpires: Expires;
      public
        destructor Destroy; override;
      published
        property Created: Created Index (IS_REF) read FCreated write FCreated;
        property Expires: Expires Index (IS_REF) read FExpires write FExpires;
      end;

      Identifier = class(TRemotable)
      private
        FSCTToken: string;
      public
      published
        property SCTToken: string Index (IS_TEXT) read FSCTToken write FSCTToken;
      end;

      SecurityContextToken = class(TRemotable)
      private
        FIdentifier: Identifier;
      public
        destructor Destroy; override;
      published
        property Identifier: Identifier Index (IS_REF) read FIdentifier write FIdentifier;
      end;

      RequestedSecurityToken = class(TRemotable)
      private
        FSecurityContextToken: SecurityContextToken;
      public
        destructor Destroy; override;
      published
        property SecurityContextToken: SecurityContextToken Index (IS_REF) read FSecurityContextToken write FSecurityContextToken;
      end;

      BiPROVersion = class(TRemotable)
      private
        FVersion: string;
      public
      published
        property Version: string Index (IS_TEXT) read FVersion write FVersion;
      end;

      RequestSecurityTokenResponse = class(TRemotable)
      private
        FTokenType: TokenType;
        FLifetime: Lifetime;
        FRequestedSecurityToken: RequestedSecurityToken;
        FBiPROVersion: BiPROVersion;
      public
        destructor Destroy; override;
      published
        property TokenType: TokenType Index (IS_REF) read FTokenType write FTokenType;
        property Lifetime: Lifetime Index (IS_REF) read FLifetime write FLifetime;
        property RequestedSecurityToken: RequestedSecurityToken Index (IS_REF) read FRequestedSecurityToken write FRequestedSecurityToken;
        property BiPROVersion: BiPROVersion Index (IS_REF) read FBiPROVersion write FBiPROVersion;
      end;

  SecurityTokenServicePortType = interface(IInvokable)
  ['{FE7EBD83-56D0-4542-5A4D-662805285ED8}']
    function  RequestSecurityToken(const TokenType: TokenType; RequestType: RequestType): RequestSecurityTokenResponse; stdcall;
  end;

    initialization
      { SecurityTokenServicePortType }
      InvRegistry.RegisterInterface(TypeInfo(SecurityTokenServicePortType), 'http://schemas.xmlsoap.org/ws/2005/02/trust', 'UTF-8');
      InvRegistry.RegisterDefaultSOAPAction(TypeInfo(SecurityTokenServicePortType), '');
      InvRegistry.RegisterInvokeOptions(TypeInfo(SecurityTokenServicePortType), ioDocument);
    //  InvRegistry.RegisterInvokeOptions(TypeInfo(SecurityTokenServicePortType), ioLiteral);
      { SecurityTokenServicePortType.RequestSecurityToken }
    //  InvRegistry.RegisterMethodInfo(TypeInfo(SecurityTokenServicePortType), 'RequestSecurityToken', '',
    //                                 '[RequestNS="http://schemas.xmlsoap.org/ws/2005/02/trust", ResponseNS="http://schemas.xmlsoap.org/ws/2005/02/trust"]');

    //  InvRegistry.RegisterParamInfo(TypeInfo(SecurityTokenServicePortType), 'RequestSecurityToken', 'RequestSecurityToken', '',
    //                                '[Namespace="http://schemas.xmlsoap.org/ws/2005/02/trust"]', IS_REF);
    //  InvRegistry.RegisterParamInfo(TypeInfo(SecurityTokenServicePortType), 'RequestSecurityToken', 'RequestSecurityTokenResponse', '',
    //                                '[Namespace="http://schemas.xmlsoap.org/ws/2005/02/trust"]', IS_REF);

      InvRegistry.RegisterMethodInfo(TypeInfo(SecurityTokenServicePortType), 'RequestSecurityToken', '',
                                     '[ReturnName="RequestSecurityTokenResponse", Namespace="http://schemas.xmlsoap.org/ws/2005/02/trust"]');
      InvRegistry.RegisterParamInfo(TypeInfo(SecurityTokenServicePortType), 'RequestSecurityToken', 'RequestSecurityTokenResponse', '',
                                    '[Namespace="http://schemas.xmlsoap.org/ws/2005/02/trust"]');

      RemClassRegistry.RegisterXSClass(BiPROVersion,
        'http://www.bipro.net/namespace/allgemein', 'BiPROVersion');
      RemClassRegistry.RegisterXSClass(Identifier,
        'http://schemas.xmlsoap.org/ws/2005/02/sc', 'Identifier');
      RemClassRegistry.RegisterXSClass(SecurityContextToken,
        'http://schemas.xmlsoap.org/ws/2005/02/sc', 'SecurityContextToken');
      RemClassRegistry.RegisterXSClass(RequestedSecurityToken,
        'http://schemas.xmlsoap.org/ws/2005/02/trust', 'RequestedSecurityToken');
      RemClassRegistry.RegisterXSClass(Created,
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd', 'Created');
      RemClassRegistry.RegisterXSClass(Expires,
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd', 'Expires');
      RemClassRegistry.RegisterXSClass(Lifetime,
        'http://schemas.xmlsoap.org/ws/2005/02/trust', 'Lifetime');

      RemClassRegistry.RegisterXSClass(RequestSecurityToken,
        'http://schemas.xmlsoap.org/ws/2005/02/trust', 'RequestSecurityToken');
      RemClassRegistry.RegisterXSClass(RequestSecurityTokenResponse,
        'http://schemas.xmlsoap.org/ws/2005/02/trust', 'RequestSecurityTokenResponse');

      RemClassRegistry.RegisterXSClass(RequestType,
        '', 'RequestType');
      RemClassRegistry.RegisterXSClass(TokenType,
        '', 'TokenType');
      RemClassRegistry.RegisterXSClass(BinarySecurityToken,
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', 'BinarySecurityToken');
      RemClassRegistry.RegisterXSClass(Security,
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', 'Security');

      InvRegistry.RegisterHeaderClass(TypeInfo(SecurityTokenServicePortType),
        Security, 'Security',
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd');

    end.

Now... that seems to work... delphi soap generates the above request, sends it to the webservice and receives a response... which I already have checked (saved it to a file in the RIO.OnAfterExecute() event). The response is ok.

but I got a problem: the result of the method RequestSecureToken (aTokenOut) is a nil value.

procedure TMainForm.btnGetTheTicket2Click(Sender: TObject);
var
  aBean: SecurityTokenServicePortType;
  aTokenOut: RequestSecurityTokenResponse;
  aSCTHeader: SecurityTokenService_2.Security;
  aCreated: TDateTime;
  aExpires: TDateTime;
  aTicket: string;
  aTokenType: TokenType;
  aRequestType: RequestType;
begin
  aBean := SecurityTokenService_2.GetSecurityTokenServicePortType;

  aSCTHeader := SecurityTokenService_2.Security.Create;
  try
    aSCTHeader.BinarySecurityToken := SecurityTokenService_2.BinarySecurityToken.Create;
    aSCTHeader.BinarySecurityToken.EncodingType := 'NS1:Base64Binary';
    aSCTHeader.BinarySecurityToken.ValueType := 'bipro:VDGTicket';
    aSCTHeader.BinarySecurityToken.VDGTicket := edVDGTicket.Text;
    (aBean as ISOAPHeaders).Send(aSCTHeader);

    aTokenType := TokenType.Create;
    try
      aTokenType.TokenType := 'http://schemas.xmlsoap.org/ws/2005/02/sc/sct';
      aRequestType := RequestType.Create;
      try
        aRequestType.RequestType := 'http://schemas.xmlsoap.org/ws/2005/02/trust/Issue';

        aTokenOut := aBean.RequestSecurityToken(aTokenType, aRequestType);
        try

// place a breakpoint here 

          aCreated := aTokenOut.Lifetime.Created.Date.AsDateTime;
          aExpires := aTokenOut.Lifetime.Expires.Date.AsDateTime;

          aTicket := aTokenOut.RequestedSecurityToken.SecurityContextToken.Identifier.SCTToken;
        finally
          FreeAndnil(aTokenOut);
        end;
      finally
        FreeAndNil(aRequestType);
      end;
    finally
      FreeAndNil(aTokenType);
    end;
  finally
    FreeAndNil(aSCTHeader);
  end;
end;

Did I forget to register anything? Do I do something wrong?

Fallback: is there a possibility to generate a wsdl file from request-xml and response-xml?


Solution

  • The problem was the interface of the method RequestSecurityToken...

      SecurityTokenServicePortType = interface(IInvokable)
      ['{FE7EBD83-56D0-4542-5A4D-662805285ED8}']
        function RequestSecurityToken(const TokenType: TokenType; RequestType: RequestType): RequestSecurityTokenResponse; stdcall;
      end;
    

    With that kind of SOAP-Response, it has to be like this:

      SecurityTokenServicePortType = interface(IInvokable)
      ['{FE7EBD83-56D0-4542-5A4D-662805285ED8}']
        procedure RequestSecurityToken(TokenType: TokenType; RequestType: RequestType;
          out Lifetime: Lifetime;
          out RequestedSecurityToken: RequestedSecurityToken;
          out BiPROVersion: BiPROVersion); stdcall;
      end;