Delphi Seattle - Sending Post Request with custom body Responds bad Request 400 - rest

I would like to send a Post Request to a Rest Server of external Provider. I Have tried with Curl and everything works perfect. Here is the CURL Code:
curl -X POST -H 'PRODUCT-KEY: SUperL0ngAndSpecialSecretCode' -H 'Content-Type: application/json' -H 'Authorization: Basic CrytedWorksalsowellwithotherget' -i 'http://myserver:8080/rest.svc/v1.0/query' --data '{
"query":"SELECT Name from Address where name like '\''%me%'\''"
}'
In Curl Everything works fine.
I have tried for a bunch of hours to get this Code working in Delphi. At this time my code looks like this:
function GetSomeInformation: string;
var
lrestrequest: TRESTRequest;
lRestClient: TRESTClient;
lRestResponce: TRESTResponse;
begin
result := '';
lRestClient := TRESTClient.Create('http://myserver:8080/rest.svc/v1.0/query');
try
lrestrequest := TRESTRequest.Create(nil);
try
lRestResponce := TRESTResponse.Create(nil);
try
lrestrequest.Client := lRestClient;
lrestrequest.Response := lRestResponce;
lrestrequest.Method := rmPost;
lrestrequest.Params.AddItem('PRODUCT-KEY',
'SUperL0ngAndSpecialSecretCode',
TRESTRequestParameterKind.pkHTTPHEADER);
lrestrequest.Params.AddItem('Content-Type', 'application/json',
TRESTRequestParameterKind.pkHTTPHEADER);
lrestrequest.Params.AddItem('query',
ansitoutf8('SELECT Name from Address where Name like ' +
quotedstr('%me%')), TRESTRequestParameterKind.pkREQUESTBODY);
lrestrequest.Execute;
if not lRestResponce.Status.Success then
showmessage(lRestResponce.StatusText + ' ' +
inttostr(lRestResponce.StatusCode))
else
result := lRestResponce.Content;
finally
lRestResponce.Free;
end;
finally
lrestrequest.Free
end;
finally
lRestClient.Free;
end;
end;
I have no idea what to do next to get the Work done? Any ideas or ways I can Debug the problem better.
--Update
Okay I Used Wireshark to check if there are any differences between the Post commands, it look like Delphi ignores or broke my Header.
In the Wireshark snippet there is a Value Content-Type. It should be
Content-Type: application/json
But with Delphi I get
Content-Type: application%2Fjson, application/x-www-form-urlencoded
And I also miss the Procduct-Key Value.
Any Suggestions?

You have to specify the kind of request in Method property of TRESTRequest
lRESTRequest.Method := TRESTRequestMethod.rmPost
Delphi ships with code samples!
http://docwiki.embarcadero.com/CodeExamples/Tokyo/en/REST.RESTDemo_Sample

after some Research and some Wireshark I got my Work done at least here ist the way I throw the parameters into my Request.
function GetSomeInformation: string;
var
lrestrequest: TRESTRequest;
lRestClient: TRESTClient;
lRestResponce: TRESTResponse;
begin
result := '';
lRestClient := TRESTClient.Create('http://myserver:8080/rest.svc/v1.0/query');
try
lrestrequest := TRESTRequest.Create(nil);
try
lRestResponce := TRESTResponse.Create(nil);
try
lrestrequest.Client := lRestClient;
lrestrequest.Response := lRestResponce;
RESTRequest1.Params.Clear;
RESTRequest1.Method:=rmpost;
RESTResponse1.RootElement := '';
lparam := RESTRequest1.Params.AddItem;
lparam.name := 'PRODUCT-KEY';
lparam.Value := 'SpecialKeyButWithSomeTrickyCharsLike==';
lparam.ContentType := ctNone;
lparam.Kind := pkHTTPHEADER;
//This one is Important otherwise the '==' will get url encoded
lparam.Options := [poDoNotEncode];
lparam := RESTRequest1.Params.AddItem;
lparam.name := 'data';
lparam.Value := '{"query":"' + SelectString + '"}';
lparam.ContentType := ctAPPLICATION_JSON;
lparam.Kind := pkGETorPOST;
lrestrequest.Execute;
if not lRestResponce.Status.Success then
showmessage(lRestResponce.StatusText + ' ' +
inttostr(lRestResponce.StatusCode))
else
result := lRestResponce.Content;
finally
lRestResponce.Free;
end;
finally
lrestrequest.Free
end;
finally
lRestClient.Free;
end;
end;
Thanks for Supporting!
PJM

Related

Consuming rest API using PL/SQL - Body not recognized

I am new to this forum and consuming Rest API's using PL/SQL. I am trying to consume a rest service from pl/sql, but the server is not able to identify the request. The same request works from postman.. so no problem in the service, something with my pl/sql code i think..
I am trying the free api provided by rapidapi.
https://rapidapi.com/weatherapi/api/weatherapi-com
My Stored Procedure
Create Or Replace Procedure TEST_REST_API
(
PURL IN VARCHAR2
)
is
req utl_http.req;
res utl_http.resp;
url varchar2(4000) := PURL;
buffer varchar2(32767);
content varchar2(8000);
begin
dbms_output.put_line('START');
UTL_TCP.close_all_connections;
DBMS_OUTPUT.put_line('2');
UTL_HTTP.set_wallet('file:/u01/app/oracle/product/11.2.0/db_1/wallet/rapidapi','wallet123');
DBMS_OUTPUT.put_line('3');
UTL_HTTP.CLEAR_COOKIES();
content := '{"q":"autp:ip"}';
--content := '';
dbms_output.put_line('content '||content);
req := utl_http.begin_request(url, 'GET',' HTTP/1.1');
utl_http.set_header(req, 'X-RapidAPI-Key', 'af1e7931bamsh3ac102afa8fef68p100423jsn8d4d3cc1325b');
utl_http.set_header(req, 'X-RapidAPI-Host', 'weatherapi-com.p.rapidapi.com');
utl_http.set_header(req, 'Content-Length', length(content));
utl_http.set_header(req, 'User-Agent', 'mozilla/4.0');
--utl_http.set_header(req, 'user-agent', 'PostmanRuntime/7.29.2');
utl_http.set_header(req, 'Content-Type', 'application/json');
utl_http.set_header(req, 'Connection','keep-alive');
--utl_http.set_header(req, 'Accept','*/*');
--utl_http.write_text(req, content);
utl_http.write_text(req, content);
--utl_http.write_text(req,'');
--insert into wstemp values (req);
res := utl_http.get_response(req);
-- process the response from the HTTP call
begin
loop
utl_http.read_line(res, buffer,TRUE);
dbms_output.put_line(buffer);
end loop;
dbms_output.put_line('Response XML:'|| cast(x_clob as varchar2));
utl_http.end_response(res);
exception
when utl_http.end_of_body
then
utl_http.end_response(res);
end;
end;
The execution script
BEGIN
TEST_REST_API
(
'https://weatherapi-com.p.rapidapi.com/ip.json'
);
END;
/
The response from server
{"error":{"code":1003,"message":"Parameter q is missing."}}
Any help would be highly appreciated.
If you're posting data then you need to use POST not GET:
req := utl_http.begin_request(url, 'POST', 'HTTP/1.1');
You also seem to have a typo in your content, with autp instead of auto:
content := '{"q":"auto:ip"}';
However, the API you're using only seems to support GET, so you can't send body content - that only applies when posting data. You will need to revert the method to GET, remove content from your test procedure, and either append the request parameter in the procedure:
req := utl_http.begin_request(url || '?q=auto:ip', 'GET',' HTTP/1.1');
or modify the URL being passed in, or add another parameter with the query parameter to append. You may need to add quotes, and might also need to escape some entities.

Sending an Email in Delphi using Indy... SSL Negotiation Failed [duplicate]

I try to send eMail with the code below (taken from http://delphiprogrammingdiary.blogspot.com/2014/12/how-to-send-email-in-delphi.html) without success.
Constantly I receive the error ("socket error 10054 connection reset by peer" twice and finally "SSL negotiation failed") at the statement "Send(IdMessage1)" although client is successfully connected with server.
procedure SendEmailClick(Sender: TObject);
var
IdMessage1: TIdMessage;
Attachmentfile: TIdAttachmentFile;
begin
// IO HANDLER SETTINGS //
With TIdSSLIOHandlerSocketOpenSSL.Create(nil) do
begin
Destination := 'mySrver.com:587';
Host := 'mySrver.com';
MaxLineAction := maException;
Port := 587;
SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
SSLOptions.Mode := sslmUnassigned;
SSLOptions.VerifyMode := [];
SSLOptions.VerifyDepth := 0;
end;
//SETTING SMTP COMPONENT DATA //
with TIdSMTP.Create(nil) do begin
Host := 'mySrver.com';
Port := 587;
Username := myMailAddress; // please change to your gmail address //
Password := myPassword;
IOHandler := TIdSSLIOHandlerSocketOpenSSL.create;
AuthType := satDefault;
UseTLS := utUseExplicitTLS;
// SETTING email MESSAGE DATA //
IdMessage1:= TIdMessage.Create(nil);
IdMessage1.Clear;
// add recipient list //
with IdMessage1.Recipients.Add do
begin
Name := 'Recipient 1';
Address := recipient1Address; // please change email address as required //
end;
//add Attachment to mail //
Attachmentfile := TIdAttachmentFile.Create(IdMessage1.MessageParts,'Τιμολόγιο 659.PDF');
IdMessage1:= TIdMessage.Create(nil);
IdMessage1.From.Address := myMailAddress; // please change to your gmail address //;
IdMessage1.Subject := 'Test Email Subject';
IdMessage1.Body.Add('Test Email Body');
IdMessage1.Priority := mpHigh;
TRY
Connect(); // no problem here. it connects always
Send(IdMessage1); // raises the error SSL negotiation failed
ShowMessage('Email sent');
Disconnect();
except on e:Exception do
begin
ShowMessage(e.Message);
Disconnect();
end;
END;
IdMessage1.Free;
IOHandler.Free;
end;
AttachmentFile.Free;
end;
Can you help me please ?
PS1. The same problem raises with any MAPI server (myServer, GMail, Yahoo etc)
PS2. The SSL DLLs are present and they are used by the same application to connect for other purpose without this problem.
You are creating a TIdSSLIOHandlerSocketOpenSSL object and configuring it, but then you discard and leak it and then create and use another TIdSSLIOHandlerSocketOpenSSL object with default settings. Get rid of the second object, use the first object instead.
You are also creating 2 TIdMessage objects, as well. Get rid of the second one.
Also, you don't need to set the Host, Port, and Destination properties on the SSLIOHandler. Connect() will handle that for you.
Try this:
procedure SendEmailClick(Sender: TObject);
var
IdSMTP: TIdSMTP;
IdMessage1: TIdMessage;
Attachmentfile: TIdAttachmentFile;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
//SETTING SMTP COMPONENT DATA //
IdSMTP := TIdSMTP.Create(nil);
try
IdSMTP.Host := 'mySrver.com';
IdSMTP.Port := 587;
IdSMTP.Username := myMailAddress; // please change to your gmail address
// Password := myPassword;
IdSMTP.AuthType := satDefault;
IdSMTP.UseTLS := utUseExplicitTLS;
// IO HANDLER Settings //
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTP);
IdSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
IdSSL.SSLOptions.Mode := sslmUnassigned;
IdSSL.SSLOptions.VerifyMode := [];
IdSSL.SSLOptions.VerifyDepth := 0;
IdSMTP.IOHandler := IdSSL;
// SETTING email MESSAGE DATA //
IdMessage1 := TIdMessage.Create(nil);
try
// add recipient list //
with IdMessage1.Recipients.Add do
begin
Name := 'Recipient 1';
Address := recipient1Address; // please change email address as required //
end;
//add Attachment to mail //
Attachmentfile := TIdAttachmentFile.Create(IdMessage1.MessageParts, 'Τιμολόγιο 659.PDF');
IdMessage1.From.Address := myMailAddress; // please change to your gmail address //;
IdMessage1.Subject := 'Test Email Subject';
IdMessage1.Body.Add('Test Email Body');
IdMessage1.Priority := mpHigh;
try
IdSMTP.Connect();
try
IdSMTP.Send(IdMessage1);
ShowMessage('Email sent');
finally
IdSMTP.Disconnect();
end;
except
on e: Exception do
begin
ShowMessage(e.Message);
end;
end;
finally
IdMessage1.Free;
end;
finally
IdSMTP.Free;
end;
end;

Connection to MongoDB Atlas with Delphi

I'm using Delphi Seattle with Firedac and I need to connect to my base which is on MongoDB Atlas.
On Firedac I only have the option to put the server's ip, but Atlas only provides a connection string.
How to connect to MongoDB Atlas with Firedac + Delphi?
PS: I tried to get the part of the connection string that references the server address, "mongodb+srv://address.address.mongodb.net/myFirstDatabase", but delphi cannot resolve this address
I was unable to get to work as well, Instead, I used the REST API instead. eg
RESTClient1.BaseURL := 'https://data.mongodb-api.com';
RESTRequest1.Client := RESTClient1;
RESTRequest1.Resource := '/app/data-cemfo/endpoint/data/v1/action/findOne';
RESTRequest1.Method := rmPOST;
RESTRequest1.AddAuthParameter('api-key', 'FHZ....your API key',
pkHTTPHEADER, [poDoNotEncode]);
RESTRequest1.AddParameter('Content-Type', 'application/json', pkHTTPHEADER);
RESTRequest1.AddParameter('Access-Control-Request-Headers', '*',
pkHTTPHEADER);
body := TJSONObject.Create;
body.AddPair ('collection', 'mycollection');
body.AddPair ('database', 'mydbs');
body.AddPair ('dataSource', mydsrc');
subObj := TJSONObject.Create;
subObj.AddPair ('numInventory', 10);
body.AddPair ('filter', subObj);
RESTRequest1.AddBody(body);
RESTRequest1.Execute;
jsonStr := RESTResponse1.JSONValue.ToString;

How to post data with a ContentType of 'multipart/form-data' in Delphi REST?

I am trying to send a request to a REST API using multipart/form-data as the content type.
I always get "HTTP/1.1 500 Internal Error" as a response.
I tried sending requests to methods that require application/x-www-form-urlencoded and had success, though.
How can I achieve getting a success response from my API using multipart/form-data?
Here is my code:
procedure TForm10.Button1Click(Sender: TObject);
var
RESTClient1: TRESTClient;
RESTRequest1: TRESTRequest;
strImageJSON : string;
Input: TIdMultipartFormDataStream;
begin
Input := TIdMultipartFormDataStream.Create;
Input.Clear;
Input.AddFormField('Email', 'tugba.xx#allianz.com.tr');
Input.AddFormField('Password', 'xxxx');
RESTClient1 := TRESTClient.Create('http://192.168.1.172:81/');
RESTRequest1 := TRESTRequest.Create(nil);
RESTRequest1.Method := TRESTRequestMethod.rmPOST;
RESTRequest1.Resource := 'api/Mobile/MobileLoginControl';
RESTRequest1.AddBody(Input,TRESTContentType.ctMULTIPART_FORM_DATA);
RESTRequest1.Client := RESTClient1;
RESTRequest1.Execute;
strImageJSON := RESTRequest1.Response.Content;
end;
Embarcadero's REST component has its own built in multipart/form-data capabilities via the TRESTRequest.AddParameter() method:
procedure TForm10.Button1Click(Sender: TObject);
var
RESTClient1: TRESTClient;
RESTRequest1: TRESTRequest;
strImageJSON : string;
begin
RESTClient1 := TRESTClient.Create('http://192.168.1.172:81/');
try
RESTRequest1 := TRESTRequest.Create(nil);
try
RESTRequest1.Method := TRESTRequestMethod.rmPOST;
RESTRequest1.Resource := 'api/Mobile/MobileLoginControl';
RESTRequest1.AddParameter('Email', 'tugba.xx#allianz.com.tr', TRESTRequestParameterKind.pkREQUESTBODY);
RESTRequest1.AddParameter('Password', 'xxxx', TRESTRequestParameterKind.pkREQUESTBODY);
RESTRequest1.Client := RESTClient1;
RESTRequest1.Execute;
strImageJSON := RESTRequest1.Response.Content;
finally
RESTRequest1.Free;
end;
finally
RESTClient1.Free;
end;
end;
You don't need to use use Indy's TIdMultiPartFormDataStream, especially when you are not using Indy's TIdHTTP.

Why does indy return unreadable data but chrome loads it fine?

I'm trying to do a GET request on one of my game server's RESTAPI. When I load it in chrome it load with the correct json response but when I try to do it with indy it just gives me a bunch of unreadable text.
URL for the request: http://129.232.180.125:28019/deaths.json
Chrome Response:
Indy Reponse:
Y;9[ÿÍZË7¼ç3öìDQ%ÿJÃÚØ,Öw òï§IµzvXÓö{Ab¿ÿöÏÝ»÷Ä!¾»ûûîýBó»»÷ßOwïï><¼îþ}§ På¢&È÷ÇÇÓË
gP¤#¹(¬Dv&©ÙEµ¸®D"`»#ëJ¥E\¦£??Þ?Ù#%¤3¨µ òrÿaCHZÝÀÕ
¯[%ö1cÓÔB)QV¡÷àõ=¢oÅt¥þóR§Ó§§Ï®Ô¶â*ëEu<wKÜôiýUØ^VµcÓ#vêâ»ÈR©hPædÛ=û±ô~¡;±8¢¢ÒtÇ/§û
Si¿·Õ
Zô1-W¥²¾KdP¡tÇXýc--[ÛàÔ-Go¼aI4]ñôãÏǯÏ_& h¶#5Gá0Ú#!`u.éöæà¿i³ÇhFÜ]Q´µjÁ
d¼Ê%½|ÔRØ\­¡Ókbê;Â$`¹^ìX»Ó`ϢѤù» i2S$Ýûöõ¯ÉÑ2 D(,öæÛý§ûÙ¯j\-AOÙ^&Gä£WK¥2\ÙW[%a»a\Ò' U½4ßß+¯>bV«ÏêUçq¡õÏIÜ
,1×çiJ¾¸¸^Ðh}_æéê{²Ú°pp¼²KÖ÷ÞNÄIß¼D÷W+K3ËûÔz:}ÚÞ5
SB^"ÕKªGè¸+¾ß^~$Õâ³6Tõ]2Õßr9¸%eàYYUrsJÏ`Êä³ë'£t8#®AB¨KϹëOAe9V ×Ís-£¬Ó鬺ùÜ\)µµ©±¢xÜj« ½ê(JRâÜ>\<M´#6Xd°ý~®i ÄCõBe;&©l³"¦}.uUè5(ÝE®V;BìðùôtúöðqÆÅ8~cÜ¡}{\h9Í©¡0²]Cèl¯s%qeÅô{Ø]zOöÞ|W-ÆöÎ%媫¦#çÎл[jѶÊÞÒÙq¤¸qZÛ/îÕ=KTÓ³ÿÒ=Go#Îì6*Z¯\W7¡ê!rM´'£¸ÇR­»[G%ëp"?+²q?!ÔU{n,QâsðÚ9÷Þòì³H¡#f<Î.ÛÂѲ­Ät$u\9KoËGpE9b+ NsQÏ>ÉIiÒQs8ôòVÖMó.Þ÷ex ½½îx!RNÇZH?×b# Ó$v¥_SfçF«Q®n`|à`«T­®Ð¦©¸×5*<?qC´/ø©¿ÑJ¬ªï´´+ýÑHjiK( ;¤øÓA#mýH߬5v ¡µR
´RI{ÊPAn,&æF ,&Õ/ônÓÚ¨­oÄÀùYó}DUÿêà}C$e õ^LYæªYßÍX«Z2»ËeOæVqWvrW1ÚP«¿_kÆ-¢¢2Ar«ö©]x¢|4Ñ¿¯Y[¹">´¸(lÉÆÓÌå/BÙ!ìPGr£`¶Ê
[!ªóèsfÄt+­.v.5_jÎê'Ô8Õ
Ö1ÒøMÜÑõâ[®ÇÄ)ãZYàÜ;Q!Ëa2Û^Zìçsá¹Ø^´QZbKnk9&ÄKºý¼ý®Úú11pÀ¦Eç°¿9r¾«ÿ®yýÆà"ø×
 (ù ?Ko¼Þ!Τ¸ò¿^/ºo¼½åzãß*A9aãÔ°¥Ù8umÁàËuni(
'-®®DÕêüý¸o¢z­äݲé/lßþËÁi-ö:IhÙ8£i?|Û×RÐØï/ÐÀ,I
°Ù¥xXIRê0¹\x9séU=ªajä ^Æ£À±b¼$ÞP6<fF!µ¡Àßù!ûí7t¨¬XRÀJï\VãØsþð»b¨´7B` FýÀJÕ´F
6Ë
èög»&·ã«¶ÛýAuR®ÝåL¬.YÌÄÿ£0æÀ%dÅ)²dhs´k¯.Ûg%ÅÂ.sâ¬Úé
zµ½ ;¸Z}»ÕUH«;ébÿ£j£%³S÷ú~X´#ýgý:¹½ 0w#ºØHâ³·,`!«G(]íä,Q¡NØm¢Î°,ÔY`Ùi;Ò¦×b¤×?BûMd¡Çdª <ï"QÕýWÏ®µ±× "6'3ý¹¦wþGSCt7Ùäk Ne¾ªÑ¿Ò(múS Ô$DifA{ª*ö¥(-gg¶a³~v(dê©=Om+á½üí±6®yf°1²ÐNi(y`B»ò¦¹ÃàuS!kµ¡¡i÷6¦ hÖ½;r"¢R»yÆ&OªoE·VU´¹)_Q9Ç¥2lÑÀâîUhñ~¼4óß4"\µnøæèî½5Ç00fúiPàýLØ~¯é§?_HÙ&Í÷;Ynnw¯8àoü¬¼^+2
My Code:
mmo1.Lines.Text := idhtp1.Get('http://129.232.180.125:28019/deaths.json');
If the server has Content-Encoding: gzip in the headers, so you must decompress the data.
Here is a full & working example (the check for gzipped content is omitted):
program SO51126032;
{$APPTYPE CONSOLE}
{$R *.res}
uses
IdHttp,
IdZLib,
Classes,
System.SysUtils;
function GetServerData(Url : String) : String;
var
Http : TIdHttp;
Strm : TMemoryStream;
OutStrm : TStringStream;
begin
Http := TIdHttp.Create(nil);
try
Http.HandleRedirects := True;
Http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
Http.Request.AcceptEncoding := 'gzip';
Strm := TMemoryStream.Create;
try
OutStrm := TStringStream.Create(TEncoding.UTF8);
try
Http.Get(URL, Strm);
Strm.Position := 0;
if TextIsSame(Http.Response.ContentEncoding, 'gzip') then
DecompressStream(Strm, OutStrm)
else
OutStrm.CopyFrom(Strm, 0);
end;
Result := OutStrm.DataString;
finally
OutStrm.Free;
end;
finally
Strm.Free;
end;
finally
Http.Free;
end;
end;
begin
try
Writeln(GetServerData('http://129.232.180.125:28019/deaths.json'));
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
* RECOMMENDED WAY *
Indy can do the hard work for you if you assign a TIdCompressorZlib to TIdHTTP, it will automatically detect gzipped content and decompress on the fly:
uses
IdHttp,
IdCompressorZLib,
System.SysUtils;
function GetServerData(const URL : String) : String;
var
Http : TIdHttp;
begin
Http := TIdHttp.Create(nil);
try
Http.HandleRedirects := True;
Http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
Http.Compressor := TIdCompressorZLib.Create(Http);
Result := Http.Get(URL);
finally
Http.Free;
end;
end;