I'm trying to use Delphi 10.2 TREST components with AWS. I have a CURL command that works:
curl -X POST --data #GetIDData.json -H "X-Amz-Target: AWSCognitoIdentityService.GetId" -H "Content-Type: application/x-amz-json-1.1" https://cognito-identity.us-east-1.amazonaws.com/
GetIDData.json contains this:
{"IdentityPoolId":"us-east-1:XXXXXXXXXXXXXXXXXXXXX"}
Successful result is this:
{"IdentityId":"us-east-1:XXXXXXXXXXXXXXXXXXXXX"}
I'd like to duplicate that result using Delphi TREST components:
...
fClient := TRESTClient.Create('https://cognito-identity.us-east-1.amazonaws.com/');
fClient.SetHTTPHeader('Content-Type', 'application/x-amz-json-1.1');
fClient.SetHTTPHeader('X-Amz-Target', 'AWSCognitoIdentityService.GetId');
fRequest := TRESTRequest.Create(nil);
fRequest.Client := fClient;
fRequest.Method := TRESTRequestMethod.rmPOST;
// fRequest.AddBody('{"IdentityPoolId":"us-east-1:XXXXXXXXXXXXXXXXXXXXX"}', ctAPPLICATION_JSON);
lJObj := TJSONObject.Create;
lJObj.AddPair('IdentityPoolId', 'us-east-1:XXXXXXXXXXXXXXXXXXXXX');
fRequest.AddBody(lJObj);
fRequest.Execute;
str := fRequest.Response.Content;
...
But the result is an error:
{"Output":"__type":"com.amazon.coral.service#UnknownOperationException","message":null},"Version":"1.0"}
Downloading OpenSSL and putting the dlls into System32 did not help.
Can anyone tell me what I'm doing wrong?
This works:
...
lClient := TRESTClient.Create('https://cognito-identity.us-east-1.amazonaws.com/');
lRequest := TRESTRequest.Create(nil);
lRequest.Client := lClient;
lRequest.Method := TRESTRequestMethod.rmPOST;
lParam := lRequest.Params.AddItem;
lParam.name := 'X-Amz-Target';
lParam.Value := 'AWSCognitoIdentityService.GetId';
lParam.ContentType := ctNone;
lParam.Kind := pkHTTPHEADER;
lParam := lRequest.Params.AddItem;
lParam.name := 'Content-Type';
lParam.Value := 'application/x-amz-json-1.1';
lParam.ContentType := ctNone;
lParam.Kind := pkHTTPHEADER;
lParam.Options := [poDoNotEncode];
lRequest.AddBody('{"IdentityPoolId":"us-east-1:XXXXXXXXXXXXXXXXXXXXX"}', ctAPPLICATION_JSON);
lRequest.Execute;
...
WireShark was not as helpful as I wanted because its doc is out of date and I'm using encryption. But the website mentioned by #Christophe Morio in this post made finding a solution a piece of cake.
Related
I'm using Delphi 10.4 (with patches 1, 2 and 3) on Windows 10 64bit to build a VCL Web Client application.
I have an API running on RAD Server, and I can get information from it using the below VCL Webclient application with no trouble. However, when I try to insert data into the API resources through a POST method, I got the following error message from the TRESRequest.Execute() method call:
No mapping for the Unicode character exists in the target multi-byte code page
Though I got this error message, the JSON data is being sent to the API and is saved to the database correctly. It seems that at a certain point of the Execute() method, an error is generated after the data has been sent to the server.
I've already tried TEncoding.UTF8.GetBytes(), TEncoding.ASCII.GetBytes(), and Unicode, without success.
This is my code:
procedure TForm1.BTPostClick(Sender: TObject);
var
strjson : string;
jo : TJSONObject;
begin
strjson :=
'{' +
'"CUST_ID": 1500,' +
'"CUST_NAME": "John Doe Max",' +
'"CUST_COUNTERTOTAL": "500",' +
'"CUST_DETAILS": "This is just a test"' +
'}';
// jo := TJSONObject.ParseJSONValue(TEncoding.Unicode.GetBytes(StrJson),0) as TJSONObject;
// jo := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(StrJson),0) as TJSONObject;
jo := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(StrJson),0) as TJSONObject;
Memo1.Clear;
Memo1.Lines.Add(jo.ToString);
RestRequest1.ClearBody;
RestRequest1.Method := rmPost;
RestResponse1.ResetToDefaults;
RestRequest1.AddBody(jo);
RestRequest1.Execute; // ==> error: No mapping for the Unicode character exists in the target multi-byte code page
end;
SERVER SIDE code:
procedure TMytestResource1.MytestPost(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
s : string;
i : integer;
jo : TJSONObject;
begin
jo := TJSONObject.Create;
jo.AddPair('Response Line 1','Apple is maçã');
jo.AddPair('Response Line 2','Foot is pé ');
jo.AddPair('Response Line 3','There is lá');
jo.AddPair('Exceção Line 3',TJsonString.Create('Exception is exceção'));
//s := jo.ToString; at this point the characters has accents
AResponse.Body.SetValue(jo, True);
end;
Configuration of REST components is as follows :
RestClient1.Accept := 'application/json, text/plain; q=0.9, text/html;q=0.8,';
RestClient1.AcceptCharset := 'utf-8;q=0.8';
RestClient1.AcceptEnconding := 'utf-8';
RestClient1.FallbackCharsetEncoding := 'utf-8';
RestClient1.ContentType := 'application/json'
RestRequest1.Accept := 'application/json, text/plain; q=0.9, text/html;q=0.8,'
RestRequest1.AcceptCharset := 'utf-8;q=0.8';
RestResponse1.ContentEncoding := '';
RestResponse1.ContentType := '';
New Server Side Code (worked fine):
procedure TMytestResource1.CadastraUser(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
s : string;
i : integer;
jo : TJSONObject;
Strm : TStringStream;
begin
jo := TJSONObject.Create;
jo.AddPair('Response Line 1','Apple is maçã');
jo.AddPair('Response Line 2','Foot is pé');
jo.AddPair('Response Line 3','There is lá');
jo.AddPair('Response Line 4','Useful is útil');
//AResponse.Headers.SetValue('Content-Type', 'application/json; charset=utf-8'); ==> This line does not make any effect on response
try
// Strm := TStringStream(jo.ToJSON, TEncoding.UTF8); ==> does not work.
Strm := TStringStream.Create(jo.ToJSON, TEncoding.UTF8);
// AResponse.Body.SetStream(Strm, 'application/json; charset=utf-8', true); ==> This line causes an error on VCL WebClient during RestRequest.Execute() method , error is "Invalid enconding name."
AResponse.Body.SetStream(Strm, 'application/json', true); // this works fine.
finally
jo.Free;
end;
end;
So now, new questions arise :
Why does this same program work fine in Delphi 10.3.1 Tokyo under Windows 7, but now in Delphi 10.4 Sydney (patches 1,2,3) on Windows 10 it requires UTF-8 encoding? Is it a Delphi 10.4 issue? Is it a Windows 10 issue?
In my original server side code, I have many Endpoints sending responses using the command AResponse.Body.SetValue(myJSONObject, True); Should I replace all of them by the two new commands?
Strm := TStringStream.Create(jo.ToJSON, TEncoding.UTF8);
AResponse.Body.SetStream(Strm, 'application/json', true);
Code of REST.Client unit where the error is occuring :
Procedure TCustomRestRequest.Execute
...
if LMimeKind <> TMimeTypes.TKind.Binary then
begin
LEncoding := TEncoding.GetEncoding(FClient.FallbackCharsetEncoding); //==> This line is returning nil, though FClient.FallbackCharsetEncoding is filled with 'utf-8'
LContentIsString := True;
end;
end
else
begin
// Even if no fallback, handle some obvious string types
if LMimeKind = TMimeTypes.TKind.Text then
LContentIsString := True;
end;
end;
if LContentIsString then
LContent := FClient.HTTPClient.Response.ContentAsString(LEncoding); // ==> this line is generating the error
finally
LEncoding.Free;
end;
...
I want to save some data into a .txt file. I can create it, but I can't write text into it because the FB stays busy.
CASE counter OF
1:
fileOpen.sNetId := '';
fileOpen.sPathName := 'C:\test\test.txt';
fileOpen.nMode := FOPEN_MODEWRITE;
fileOpen.bExecute := TRUE;
fileOpen.tTimeout := T#200MS;
fileOpen();
counter := 2;
2:
IF NOT fileOpen.bBusy AND NOT fileOpen.bError THEN (* bBusy stays true*)
counter := 3;
END_IF
3:
fileOpen.bExecute := FALSE;
fbPutFile(sNetId := '', hFile := fileOpen.hFile, sLine := 'FOO', bExecute := TRUE, tTimeout := INT_TO_TIME(200), bBusy =>, bError =>, nErrId =>);
END_CASE
I get into step 2, but I can't get into step 3. Where is the problem?
It's because you have stopped calling the function block for opening the file. What you're doing here is that you're calling the FB_FileOpen in step 1, but then stop calling it. The bBusy-flag goes high (true) in step1, but can never be changed to anything else as you never call the FB again. You need to make a call to it in step2 as well. Remember that bExecute is also a trigger for the ADS-command, so once you've done it in step 2, you can set the bExecute-input to FALSE in step2 and forward as you've already triggered the command.
...
2:
fileOpen(bExecute := false);
IF NOT fileOpen.bBusy AND NOT fileOpen.bError THEN (* bBusy stays true*)
counter := 3;
END_IF
...
Also remember that Beckhoffs FB_FileOpen assumes that the directory C:\test exists (though not the file test.txt if you open it with FOPEN_MODEWRITE it will be automatically created). To create all necessary directores (if they don't already exist) use TwinCATs FB_CreateDir.
I use below code to send email using AHK. It works when I use literal strings in subject and message body. But when I try to use %variable%, the resultant email received has blank subject/message body.
Order :=
Order = %Order% `n FAX MESSAGE
Order = %Order% `n
Order = %Order% `n Sent: %DateString% %TimeString%
pmsg := ComObjCreate("CDO.Message")
pmsg.From := """Lens Shapers"" <fax#l******s.com>"
pmsg.To := "k*******r#gmail.com"
pmsg.BCC := ""
pmsg.CC := ""
pmsg.Subject := "Lenses are Ready" **;THIS SUBJECT IS TRANSMITTED GOOD**
pmsg.TextBody := %Order% **;THIS MESSAGE BODY IS BLANK WHEN EMAIL IS RECEIVED**
sAttach := ""
fields := Object()
fields.smtpserver := "smtp.gmail.com" ; specify your SMTP server
fields.smtpserverport := 465 ; 25
fields.smtpusessl := True ; False
fields.sendusing := 2 ; cdoSendUsingPort
fields.smtpauthenticate := 1 ; cdoBasic
fields.sendusername := "k*****f#l******s.com"
fields.sendpassword := "PASSWORD"
fields.smtpconnectiontimeout := 60
schema := "http://schemas.microsoft.com/cdo/configuration/"
pfld := pmsg.Configuration.Fields
For field,value in fields
pfld.Item(schema . field) := value
pfld.Update()
Loop, Parse, sAttach, |, %A_Space%%A_Tab%
pmsg.AddAttachment(A_LoopField)
pmsg.Send()
It should work, but when you use a variable in an expression, you don't use the %'s and when you use ":=" that is an assignment of an expression. So try:
pmsg.TextBody := Order
As that should work. If you also need text elements, you need to use quotes around literals and the dot concatenation:
pmsg.TextBody := "Order: " . Order . "`n`n" ; also follows with 2 new lines
Hth,
Is there a better login method in AutoHotkey to do this? Or perhaps a way to allow unsecured logins to Google? I have enabled less secure apps on the Google account settings.
AutoHotkey error:
This message could not be sent to SMTP server. The transport error codwe was 0x80040217. The server response was not available.
Gmail response (some info redacted):
Sign-in attempt prevented
Hi [redacted],
Someone just tried to sign in to your Google Account [redacted]#gmail.com from an app that doesn't meet modern security standards.
...
AutoHotkey script (some info redacted):
pmsg := ComObjCreate("CDO.Message")
pmsg.From := """John Doe"" <John.Doe#gmail.com>"
pmsg.To := "Jane.Doe#gmail.com, Joe.Schmo#gmx.com"
pmsg.BCC := "" ; Blind Carbon Copy, Invisible for all, same syntax as CC
pmsg.CC := "redacted#gmail.com" ; Somebody#somewhere.com, Other-somebody#somewhere.com
pmsg.Subject := "See below"
;pmsg.TextBody := ""
pmsg.HtmlBody := "<html><head><title>Hello</title></head><body><table border='1'><tr><td>row 1, cell 1</td><td>row 1, cell 2</td></tr><tr><td>row 2, cell 1</td><td>row 2, cell 2</td></tr></table> </body></html>"
;sAttach := "" ; can add multiple attachments, the delimiter is |
fields := Object()
fields.smtpserver := "smtp.gmail.com" ; specify your SMTP server
fields.smtpserverport := 465 ; 25
fields.smtpusessl := True ; False
fields.sendusing := 2 ; cdoSendUsingPort
fields.smtpauthenticate := 1 ; cdoBasic
fields.sendusername := "redacted#gmail.com"
fields.sendpassword := "redacted"
fields.smtpconnectiontimeout := 60
schema := "http://schemas.microsoft.com/cdo/configuration/"
pfld := pmsg.Configuration.Fields
For field,value in fields
pfld.Item(schema . field) := value
pfld.Update()
Loop, Parse, sAttach, |, %A_Space%%A_Tab%
pmsg.AddAttachment(A_LoopField)
pmsg.Send()
return
Hmm, maybe you need the quotes around the formed schema-field:
pfld.Item("""" . schema . field . """") := value
Or try '"' to get the quotes.
Also, try port 25 and/or username w/o the #gmail part (but I think what you have is correct for gmail).
Hth,
I need to send a RST flag in one socket, is it possible?
When I close the TIdTCPClient, the TCP server program tells me that the connection is still alive, and after 2 minutes the connection disappears.
I'm doing this :
TCPCliente := TIdTCPClient.Create(nil);
TCPCliente.Host := edtIP.Text;
TCPCliente.Port := 492;
TCPCliente.ConnectTimeout := 1000;
TCPCliente.Connect;
TCPCliente.Socket.WriteLnRFC(TextoEnvio, IndyTextEncoding_8Bit);
Texto := TCPCliente.Socket.ReadLn(IndyTextEncoding_8Bit);
TCPCliente.Socket.Close;
TCPCliente.Disconnect;
TCPCliente.Free;
I'm using RAD Studio XE8.