英文:
How do I implement SSL in Delphi to connect to a REDCap API server?
问题
我试图使用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];
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';
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;
英文:
I am trying to use XE7 to connect to an in-house REDCap server. REDCap has a detailed description of the API at https://education.arcus.chop.edu/redcap-api/ and a test server at https://bbmc.ouhsc.edu/redcap/api with a test token key. There is assistance at https://mran.microsoft.com/snapshot/2015-08-18/web/packages/REDCapR/vignettes/TroubleshootingApiCalls.html in R.
I can connect to the test site with Curl and PostMan. My problem is how to implement this in Delphi with SSL.
The Curl script from PostMan:
<!-- language: none -->
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'
After much searching, this is my Delphi code. What have I missed? IdLogFile1
is a component on the form.
<!-- language: delphi -->
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;
答案1
得分: 1
你已经正确设置了TLS连接(假设Indy可以找到适当的OpenSSL DLLs)。
但您没有正确设置数据参数。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;
英文:
You are setting up the TLS connection correctly (provided the appropriate OpenSSL DLLs are available where Indy can find them).
What you are not setting up correctly is your data parameters. Curl's --data-urlencode
command puts the data in the HTTP request body, not in the HTTP headers. So you need to put the data in the TStringList
that you are posting (TIdHTTP
will handle the url-encoding for you).
Try this instead:
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;
答案2
得分: 0
Two simpler alternatives with System.Net.HttpClient and mORMot. Both work as expected with same results. Tested with 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.
英文:
Two simpler alternatives with System.Net.HttpClient and mORMot . Both work as expected with same results. Tested with 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论