如何在 Delphi 中实现SSL以连接到REDCap API服务器?

8tntrjer  于 2023-06-05  发布在  其他
关注(0)|答案(2)|浏览(399)

我正在尝试使用XE7连接到内部REDCap服务器。REDCap详细描述了https://education.arcus.chop.edu/redcap-api/上的API和https://bbmc.ouhsc.edu/redcap/api上的带有测试令牌密钥的测试服务器。R中的https://mran.microsoft.com/snapshot/2015-08-18/web/packages/REDCapR/vignettes/TroubleshootingApiCalls.html有帮助。
我可以用Curl和PostMan连接到测试现场。我的问题是如何在 Delphi 中使用SSL实现这一点。
PostMan的Curl脚本:

curl --location 'https://bbmc.ouhsc.edu/redcap/api/' \
--data-urlencode 'token=9A81268476645C4E5F03428B8AC3AA7B' \
--data-urlencode 'content=record' \
--data-urlencode 'action=export' \
--data-urlencode 'format=csv' \
--data-urlencode 'rawOrLabel=label'

经过大量搜索,这是我的 Delphi 代码。我错过了什么?IdLogFile1是表单上的一个组件。

function TForm1.IdSSLIOHandlerSocketOpenSSL1VerifyPeer(Certificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
begin
   showmessage('at  IOhandler');                      
     Result := true;                             // always returns true
end;

procedure TForm1.idHTTP2BtnClick(Sender: TObject);
var post      : string;
    Params    : TStringList;
    idHTTP    : TIdHTTP;
    SSL1      : TIdSSLIOHandlerSocketOpenSSL;
    status    : integer;
    response : TstringStream;
begin
   params   := TStringList.Create;
   idHTTP   := TIdHTTP.Create(nil);
   SSL1     := TIdSSLIOHandlerSocketOpenSSL.Create(idHTTP);
   response  := TstringStream.create;

   SSL1.SSLOptions.Mode        := sslmClient ;
   SSL1.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2 ];// [  sslvSSLv3,  sslvSSLv23,sslvSSLv2, sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
   SSL1.SSLOptions.VerifyDepth := 0;
   SSL1.OnVerifyPeer           := IdSSLIOHandlerSocketOpenSSL1VerifyPeer;
   SSL1.SSLOptions.VerifyMode  := [ ];
   idHTTP.IOHandler            := SSL1;

   memo1.Lines.clear;

   idHTTP.ReadTimeout                 := 3000;
   idHTTP.ConnectTimeout              := 3000;
   idHttp.Request.BasicAuthentication := false;

   try

     idHTTP.HandleRedirects := true;
     idHTTP.Intercept       := IdLogFile1;
     IdLogFile1.Active      := true;

     IdHttp.Request.CustomHeaders.Clear;

 
     IdHttp.Request.CustomHeaders.Values['token']          := '9A81268476645C4E5F03428B8AC3AA7B';
     IdHttp.Request.CustomHeaders.Values['content']        := 'record';
     IdHttp.Request.CustomHeaders.Values['action']         := 'export';
     IdHttp.Request.CustomHeaders.Values['format']         := 'csv';
     IdHttp.Request.CustomHeaders.Values['rawOrLabel']     := 'label';
     IdHttp.Request.CustomHeaders.Values['verify_ssl']     := 'false';
     IdHttp.Request.CustomHeaders.Values['ssl_verify']     := 'false'; //various verify options ?
     IdHttp.Request.CustomHeaders.Values['ssl_verifypeer'] := 'false';

 
     idHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
     IdHTTP.Request.Charset     := 'utf-8';
     idHTTP.HTTPOptions         := [hoKeepOrigProtocol, hoForceEncodeParams];

     idHTTP.Post('https://bbmc.ouhsc.edu/redcap/api/', params, response );

   finally
        memo1.Lines.add(' ');
        memo1.lines.add(idHTTP.ResponseText);
        memo1.Lines.add(' ');
        status           := idHTTP.ResponseCode;
        memo1.Lines.Add('code: ' + inttostr(status));
 
        idhttp.Disconnect;
 
   end;
   Params.Free;
   SSL1.Free;
   idHTTP.Free;
   response.Free;
end;
eoxn13cs

eoxn13cs1#

您正在正确设置TLS连接(前提是Indy可以找到 * 适当的 * OpenSSL DLL)。
您没有正确设置的是数据参数。Curl的--data-urlencode命令将数据放在HTTP请求正文中,而不是HTTP头中。因此,您需要将数据放入正在发布的TStringList中(TIdHTTP将为您处理URL编码)。
试试这个:

procedure TForm1.idHTTP2BtnClick(Sender: TObject);
var
  params    : TStringList;
  idHTTP    : TIdHTTP;
  idSSL     : TIdSSLIOHandlerSocketOpenSSL;
  status    : integer;
  response  : string;
begin
  params := TStringList.Create;
  try
    idHTTP := TIdHTTP.Create(nil);
    try
      idSSL := TIdSSLIOHandlerSocketOpenSSL.Create(idHTTP);    

      idSSL.SSLOptions.Mode        := sslmClient ;
      idSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2 ];
      idSSL.SSLOptions.VerifyDepth := 0;
      idSSL.OnVerifyPeer           := IdSSLIOHandlerSocketOpenSSL1VerifyPeer;
      idSSL.SSLOptions.VerifyMode  := [ ];
      idHTTP.IOHandler := idSSL;

      Memo1.Lines.Clear;

      idHTTP.ReadTimeout                 := 3000;
      idHTTP.ConnectTimeout              := 3000;
      idHTTP.Request.BasicAuthentication := false;

      try    
        idHTTP.HandleRedirects := true;
        idHTTP.Intercept       := IdLogFile1;
        IdLogFile1.Active      := true;

        params.Add('token=9A81268476645C4E5F03428B8AC3AA7B');
        params.Add('content=record');
        params.Add('action=export');
        params.Add('format=csv');
        params.Add('rawOrLabel=label');

        idHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
        idHTTP.Request.Charset     := 'utf-8';
        idHTTP.HTTPOptions         := [hoKeepOrigProtocol, hoForceEncodeParams];

        response := idHTTP.Post('https://bbmc.ouhsc.edu/redcap/api/', params);    
      finally
        Memo1.Lines.Add(' ');
        Memo1.Lines.Add(idHTTP.ResponseText);
        Memo1.Lines.Add(' ');
        status := idHTTP.ResponseCode;
        Memo1.Lines.Add('code: ' + IntToStr(status));
      end;
    finally
      idHTTP.Free;
    end;
  finally
    params.Free;
  end;
end;
j2datikz

j2datikz2#

两个更简单的替代方案是System.Net.HttpClient和mORMot。两者都按预期工作,结果相同。用 Delphi 11测试。

program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Classes,
  System.Net.HttpClient,
  Syncommons,
  Syncrtsock;

var
  http : TWinHttp;
  ResHeader,ResData : SockString;
  Status : cardinal;

  Client: THTTPClient;
  Response: IHTTPResponse;
  params : TStringList;
begin
    /// mORMot alternative
    http := TWinHttp.Create('bbmc.ouhsc.edu','443',true);
    try

      Status := http.Request('/redcap/api/',
                             'POST'
                             ,0,
                             'Content-Type: application/x-www-form-urlencoded',
                             'token=9A81268476645C4E5F03428B8AC3AA7B&content=record&action=export&format=csv&rawOrLabel=label',
                             '',
                             ResHeader,
                             ResData);
    finally
      http.Free;
    end;
    writeln(formatUTF8('mORMot -> Status= % Response=%',[Status , resdata ]));

    /// HttpClient alternative
    Client := THTTPClient.Create();
    try
      params := TStringList.Create;
      try
        params.Add('token=9A81268476645C4E5F03428B8AC3AA7B');
        params.Add('content=record');
        params.Add('action=export');
        params.Add('format=csv');
        params.Add('rawOrLabel=label');
        Client.CustHeaders.Add('Content-Type' , 'application/x-www-form-urlencoded');
        Response := Client.Post('https://bbmc.ouhsc.edu/redcap/api/', params);
        writeln(formatUTF8('HttpClient -> Status= % Response=%',[Response.StatusCode, Response.ContentAsString]));

        Assert(Resdata = Response.ContentAsString); // checked!
      finally
        params.Free;
      end;
    finally
      Client.Free;
    end;
    readln;
end.

相关问题