delphi using IdHTTP.GET to send JSON data - json

I have a little problem with sending JSON data to a PHP file.
I am using XE10 (update 1) with Indy 10.6.2.
iDHTTP contenttype is application/json
Contentencoding is utf-8
I have a variable JS with correct JSON data.
I have a variable AD with valid adres to the PHP file.
If I put the output of AD + JS in the browser my PHP file is doing what it should do, i.e. creating a file with the JSON values.
But, when I do idHTTP.GET(AD + JS);, the PHP file creates an empty file.
So the PHP scipt gets called but without the JSON data.
Can someone help me PLEASE, why the data is not sending.
BTW Can I use the GET method (If not then how can I do this)?
Here is the code:
Procedure Twebform.NaPost(k : TJSONObject );
var
str : WideString;
begin
str := 'http://www.mywebsite.com/test.php?value='+k.ToString;
try
IdHTTPJ.GET(str);
except on E:Exception do
begin
ShowMessage(E.Message);
end;
end;
end;

Related

TRESTRequest JSON POST issue in Delphi

I'm trying to send JSON POST request to an API using a TRESTClient (Delphi 10.4/Windows 10), but I'm receiving a 409 conflict indicating "No input found". Using the same code in (Delphi XE6/Windows 10) I get a REST request failed: Socket Error # 10054 Connection reset by peer.. Using WireShark I was able to see that Delphi XE6 was sending the request with TLS 1.0 and Delphi 10.4 is using TLS v1.2. The API service accepts only TLS v1.2. Is there a way to use these REST components in Delphi XE6 to deliver the request with TLS 1.2?
I beleived my problem was that I was JSON encoding all the input with the code:
// Assign JSON
sl := TStringList.Create;
sl.Add('locations=[{"address":"Test1","lat":"52.05429","lng":"4.248618"},{"address":"Test2","lat":"52.076892","lng":"4.26975"},{"address":"Test3","lat":"51.669946","lng":"5.61852"},{"address":"Sint-Oedenrode, The Netherlands","lat":"51.589548","lng":"5.432482"}]');
and then assigning it to the Request Body with RESTRequest.AddBody(sl);, whereas the API only expects the value of the locations parameter to be JSON.
I have updated the code to assign the JSON to a String and used the RESTRequest.Params.Items[0].name := 'locations'; and RESTRequest.Params.Items[0].Value := s; to add the string to the request body.
Here's my updated code:
procedure TMainForm.btn_RouteXL_PostClick(Sender: TObject);
var
s, sResult, AError: String;
jsObj: TJSONObject;
AJSONValue, jsResponse: TJSONValue;
AParameter: TRESTRequestParameter;
AStatusCode: Integer;
begin
//ResetRESTComponentsToDefaults;
s := '[{"address":"Test1","lat":"52.05429","lng":"4.248618"},{"address":"Test2","lat":"52.076892","lng":"4.26975"},{"address":"Test3","lat":"51.669946","lng":"5.61852"},{"address":"Sint-Oedenrode, The Netherlands","lat":"51.589548","lng":"5.432482"}]';
RESTClient.BaseURL := 'https://api.routexl.com/tour';
RESTClient.ContentType := 'ctAPPLICATION_X_WWW_FORM_URLENCODED';
RESTClient.Authenticator := HTTPBasic_RouteXL;
RESTRequest.Method := TRESTRequestMethod.rmPOST;
RESTRequest.Params.AddItem; //Adds a new Parameter Item
RESTRequest.Params.Items[0].name := 'locations'; //sets the name of the parameter. In this case, since I need to use 'locations=' on the request, the parameter name is locations.
RESTRequest.Params.Items[0].Value := s; //Adds the value of the parameter, in this case, the JSON data.
RESTRequest.Params.Items[0].ContentType := ctAPPLICATION_X_WWW_FORM_URLENCODED; //sets the content type.
RESTRequest.Params.Items[0].Kind := pkGETorPOST; //sets the kind of request that will be executed.
RESTRequest.Execute;
try
memo_ResponseData.Lines.Add('************JSONText*******************');
memo_ResponseData.Lines.Add(RESTResponse.JSONValue.ToString);
//Memo1.Lines.Add('JSONValue :'+RESTResponse.JSONValue.Value);
memo_ResponseData.Lines.Add('StatusText :'+RESTRequest.Response.StatusText );
memo_ResponseData.Lines.Add('StatusCode :'+IntToStr(RESTRequest.Response.StatusCode ));
memo_ResponseData.Lines.Add('ErrorMesage:'+RESTRequest.Response.ErrorMessage);
memo_ResponseData.Lines.Add('************Content*******************');
memo_ResponseData.Lines.Add(RESTRequest.Response.Content);
memo_ResponseData.Lines.Add('************Headers*******************');
memo_ResponseData.Lines.Add(RESTRequest.Response.Headers.Text);
except
on E: Exception do
begin
memo_ResponseData.Lines.Add('************Error*******************');
memo_ResponseData.Lines.Add('Error : ' +E.Message);
memo_ResponseData.Lines.Add(E.ClassName);
memo_ResponseData.Lines.Add('************Error*******************');
memo_ResponseData.Lines.Add('Error : '+RESTResponse.Content);
end;
end;
end;
I'm able to get it to work in Postman using a key-value pair, by assigning the key as locations and the value as the contents of sl as seen in the screenshot. But I'm unable to translate that to Delphi.

I need to know where I'm wrong in json object to POST using TIdHTTP in Delphi

source gives socket error 14001, WITH OBJ JSON PARAM MESSAGE FOR POST
jso := TlkJSONobject.Create; // (data) as TlkJSONobject;
jso.Add('InvoiceNumber', '');
jso.Add('POSID', '910441');
jso.add('USIN', ePOSNo.Text);
jso.add('DATETIME', eDate.Text);
IdHTTP1.Request.Accept := 'application/json';
IdHTTP1.Request.ContentType := 'application/json';
{ Call the Post method of TIdHTTP and read the result into TMemo }
Memo1.Lines.Text := IdHTTP1.Post('http://localhost;8524/api/IMSFISCAL/GetInvoiceNumberByModel', JSO);
json cannot be passed as tstream
need help on it
There is no way the code you showed can produce a socket error (let alone error 14001, which is not even a socket error) since the code won't even compile!
The TIdHTTP.Post() method does not have an overload that accepts a TlkJSONobject as input. How could it? TlkJSONobject comes from a completely different 3rd party library, it is not part of the RTL or Indy. The only things you can POST with TIdHTTP are:
TStrings-derived types
TStream-derived types, including Indy's TIdMultiPartFormDataStream
a file specified by a String filename
In this case, you need to use a TStream to post JSON stored in memory. It is your responsibility to save your TlkJSONobject content to a suitable TStream of your choosing. That is outside the scope of Indy. For instance, you can use TlkJSON.GenerateText() to get the JSON into a String and then POST it using a TStringStream.
On a side note, the URL you are passing to TIdHTTP.Post() is malformed. The correct delimiter between a hostname and port number is a colon (:), not a semicolon (;).
With that said, try this:
jso := TlkJSONobject.Create;
jso.Add('InvoiceNumber', '');
jso.Add('POSID', '910441');
jso.add('USIN', ePOSNo.Text);
jso.add('DATETIME', eDate.Text);
IdHTTP1.Request.Accept := 'application/json';
IdHTTP1.Request.ContentType := 'application/json';
{ Call the Post method of TIdHTTP and read the result into TMemo }
PostData := TStringStream.Create(TlkJSON.GenerateText(jso), TEncoding.UTF8);
try
Memo1.Lines.Text := IdHTTP1.Post('http://localhost:8524/api/IMSFISCAL/GetInvoiceNumberByModel', PostData);
finally
PostData.Free;
end;

Decoding and comparing JSON with accented char

I have an IntraWeb app. In the HTML template, I have Javascript creating a JSON document.
This JSON is sent to the IntraWeb backend and I receive the JSON as:
{"order":"Razão Social"}
I parse the JSON and put "Razão Social" in a var _order.
My problem is when I try to compare that value with a string, it fails. I am having some problem with the encoding. The line
if uppercase(_order) = 'RAZÃO SOCIAL' then
is always false.
I put a breakpoint and I can see the accented char is not OK.
s:=aParams.Values['xorder'];
if s<>'' then begin
jso := TJSonObject.ParseJSONValue(TEncoding.UTF8.GetBytes(s),0) as TJSONObject;
try
jso.TryGetValue<string>('order',_order);
finally
jso.free;
end;
end;
if uppercase(_order) = 'RAZÃO SOCIAL' then
_order:='Order by A.razao_social ';
UpperCase supports ASCII characters only. Instead compare string case insensitively using AnsiCompareText or AnsiSameText, which are aware of Unicode.

Does SuperObject have UTF-8 support

I have been using superobject for all my json parsing needs and today I ran into a bit of a problem that I cannot seem to fix. I downloaded a json file that had an entry in it that looked like this: "place" : "café"and when I tried to parse the file and show it in a messagebox the word café turned out like this: café which tells me that the there is some kind of conversion failure going on when the file was parsed using superobject so before I invest any more time in this library, I would like to know if it supports UTF-8 and if so, how would I go about enabling it.
BTW, The pseudo code I am using to parse the file looks something like this:
uses
SuperObject
...
const
jsonstr = '{ "Place" : "café" }';
...
var
SupOB : ISuperObject;
begin
SupOB := SO(jsonstr);
ShowMessage(SupOB['Place'].AsString);
end;
Is the conversion failing because I am casting the object as a string? I tried also using AsJsonto see if that would have any effect, but it did not so I am not sure what is needed to make objects like these display as they are intended and would appreciate some help. Finally, I have checked and verified that the original file that is being parsed is indeed encoded as UTF-8.
You say you are parsing a file, but your example is parsing a string. That makes a big difference, because if you are reading file data into a string first, you are likely not reading the file data correctly. Remember that Delphi strings use UTF-16 in Delphi 2009 and later, but use ANSI in earlier versions. Either way, not UTF-8. So if your input file is UTF-8 encoded, you must decode its data to the proper string encoding before you can then parse it. café is the UTF-8 encoded form of café being mis-interpreted as ANSI.
Reading and writing files json encoded utf8. Tested on Delphi 2007.
function ReadSO(const aFileName: string): ISuperObject;
var
input: TFileStream;
output: TStringStream;
begin
input := TFileStream.Create(aFileName, fmOpenRead, fmShareDenyWrite);
try
output := TStringStream.Create('');
try
output.CopyFrom(input, input.Size);
Result := TSuperObject.ParseString(PWideChar(UTF8ToUTF16(output.DataString)), true, true);
finally
output.Free;
end;
finally
input.Free;
end;
end;
procedure WriteSO(const aFileName: string; o: ISuperObject);
var
output: TFileStream;
input: TStringStream;
begin
input := TStringStream.Create(UTF16ToUTF8(o.AsJSon(true)));
try
output := TFileStream.Create(aFileName, fmOpenWrite or fmCreate, fmShareDenyWrite);
try
output.CopyFrom(input, input.Size);
finally
output.Free;
end;
finally
input.Free;
end;
end;
Functions UTF8ToUTF16 and UTF16ToUTF8 from unit JclConversions http://sourceforge.net/projects/jcl/.

Does the ulkJSON library have limitations when dealing with base64 in Delphi 7?

I'm working on a project that is using Delphi 7 to consume RESTful services. We are creating and decoding JSON with the ulkJSON library. Up to this point I've been able to successfully build and send JSON containing a base64 string that exceed 5,160kb. I can verify that the base64 is being received by the services and verify the integrity of the base64 once its there. In addition to sending, I can also receive and successfully decode JSON with a smaller (~ 256KB or less) base64.
However I am experiencing some issues on the return trip when larger (~1,024KB+) base64 is involved for some reason. Specifically when attempting to use the following JSON format and function combination:
JSON:
{
"message" : "/9j/4AAQSkZJRgABAQEAYABgAAD...."
}
Function:
function checkResults(JSONFormattedString: String): String;
var
jsonObject : TlkJSONObject;
iteration : Integer;
i : Integer;
x : Integer;
begin
jsonObject := TlkJSONobject.Create;
// Validate that the JSONFormatted string is not empty.
// If it is empty, inform the user/programmer, and exit from this routine.
if JSONFormattedString = '' then
begin
result := 'Error: JSON returned is Null';
jsonObject.Free;
exit;
end;
// Now that we can validate that this string is not empty, we are going to
// assume that the string is a JSONFormatted string and attempt to parse it.
//
// If the string is not a valid JSON object (such as an http status code)
// throw an exception informing the user/programmer that an unexpected value
// has been passed. And exit from this routine.
try
jsonObject := TlkJSON.ParseText(JSONFormattedString) as TlkJSONobject;
except
on e:Exception do
begin
result := 'Error: No JSON was received from web services';
jsonObject.Free;
exit;
end;
end;
// Now that the object has been parsed, lets check the contents.
try
result := jsonObject.Field['message'].value;
jsonObject.Free;
exit;
except
on e:Exception do
begin
result := 'Error: No Message received from Web Services '+e.message;
jsonObject.Free;
exit;
end;
end;
end;
As mentioned above when using the above function, I am able to get small (256KB and less) base64 strings out of the 'message' field of a JSON object. But for some reason if the received JSON is larger than say 1,024kb the following line seems to just stop in its tracks:
jsonObject := TlkJSON.ParseText(JSONFormattedString) as TlkJSONobject;
No errors, no results. Following the debugger, I can go into the library, and see that the JSON string being passed is not considered to be JSON despite being in the format listed above. The only difference I can find between calls that work as expected and calls that do not work as expect appears to be the size of base64 being transmitted.
Am I missing something completely obvious and should be shot for my code implementation (very possible)? Have I missed some notation regarding the limitations of the ulkJSON library? Any input would be extremely helpful. Thanks in advance stack!
So after investigating this for hours over the course of some time, I did discover that the library indeed was working properly and there was no issue.
The issue came down to the performance of my machine as it was taking on average 215802 milliseconds (3.5967 minutes) to process a moderately sized image (1.2 meg) in base64 format. This performance scaled according to the size of the base64 string (faster for smaller, longer for larger).