Delphi 没有 WSDL 的 SOAP:为什么我的函数结果为 nil

Delphi SOAP without WSDL: why is my function result nil

我尝试实现一个 soap 客户端,它应该调用这样的服务:

请求-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>

回复:

<?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>

是的,还有一个 WSDL 文件,但是 Delphi 的代码生成器不会为其中的所有内容生成代码... 生成的代码缺少两个相关的类型定义......并且不可能获得正确的 wsdl......所以我决定自己编写接口单元:

      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.

现在...这似乎有效...delphi soap 生成上述请求,将其发送到网络服务并收到响应...我已经检查过(将其保存到文件中在 RIO.OnAfterExecute() 事件中)。反应还可以。

但是我遇到了一个问题:方法 RequestSecureToken (aTokenOut) 的结果是一个 nil 值。

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;

我是不是忘记注册了什么?我做错了什么吗?

回退:是否有可能从请求-xml 和响应-xml 生成 wsdl 文件?

问题是 RequestSecurityToken 方法的接口...

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

对于那种SOAP-Response,它必须是这样的:

  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;