I am new to REST in Delphi and I am wanting to view the content as text that is about to be posted. When attempting to use TJson.ObjectToJsonString I get a Stack Overflow error.
The code I am using is:
var
RestClient: TRestClient;
RestRequest: TRestRequest;
Param: TRESTRequestParameter;
begin
Memo1.lines.clear;
RestClient := TRestClient.create(nil);
RestRequest := TRestRequest.create(nil);
RestClient.Params.clear;
RestRequest.Client := RestClient;
RestRequest.Params.clear;
RestRequest.ClearBody;
RestRequest.Method := TRESTRequestMethod.rmPOST;
RestClient.BaseURL := 'http://my.server/MyService';
RestClient.ContentType := 'application/json';
Param := RestClient.Params.AddItem('my-webservice-password', 'MySecret');
Param.Kind := pkHTTPHEADER;
RestClient.Params.AddItem('Param1', 'MyFistParam');
RestClient.Params.AddItem('Param2', 'MySecondParam');
RestClient.Params.AddItem('3rdParam', 'true');
Memo1.lines.text := TJson.ObjectToJsonString(RestClient); // <<<<< Stack Overflow here
RestRequest.Execute;
end;
Can someone please advise why this would be creating a stack overflow, or alternatively what I should be doing to view the JSON request that is about to be submitted?
Thanks & Regards
Adam
Try using REST Debugger from tools menu . it is easier and show the results directly .
Related
I am working with a Delphi (10.2.2) Client app that calls API functions on a Delphi REST Server (WebModule). At this point I have 10s of these types of API calls that perform all relevant HTTP verbs (POST, GET, PUT, DELETE) and they all work fine, EXCEPT for calls where I need to upload a JSON object for INSERT/EDITs on the Server. I am not sure what I am doing wrong, so if anyone could look at my attempt and let me know what I'm missing?
var
REQ : TRESTRequest;
RESP : TRESTResponse;
NewPL : TJSONObject;
strOut : String;
begin
try
REQ := TRESTRequest.Create(Application);
RESP := TRESTResponse.Create(Application);
REQ.Response := RESP;
REQ.Resource := 'packlist';
REQ.Client := DM1.RESTClient;
REQ.Method := TRESTRequestMethod.rmPOST;
//Create JSON from UI
NewPL := TJSONObject.Create;
NewPL.AddPair('PL_NO', edtPLNo.Text);
NewPL.AddPair('PL_DATE', edtPLDate.Text);
NewPL.AddPair('SHIP_NO', edtShipmentNo.Text);
NewPL.AddPair('SHIPPER', memShipper.Text);
NewPL.AddPair('CONSIGNEE', memConsignee.Text);
NewPL.AddPair('VEHICLE_REG_NO', edtVehicleRegNo.Text);
NewPL.AddPair('ETD', DateTimeToStr(dateETD.Date));
NewPL.AddPair('ETA', DateTimeToStr(dateETA.Date));
NewPL.AddPair('TOTAL_QTY', edtTotalQty.Text);
NewPL.AddPair('TOTAL_WEIGHT', edtTotalWeight.Text);
NewPL.AddPair('TOTAL_VOLUME', edtTotalVol.Text);
if chkPLComplete.Checked then
NewPL.AddPair('COMPLETE', '1')
else
NewPL.AddPair('COMPLETE', '0');
NewPL.AddPair('SUPPLIER_NO', edtSuppNo5.Text);
NewPL.AddPair('DEBTOR_ACC_NO', '');
NewPL.AddPair('WAYBILL_NO', edtWaybillNo.Text);
strOut := NewPL.ToString;
REQ.AddBody(NewPL); //AV occurs here
REQ.Execute;
DisplayPL(NewPL);
finally
REQ.DisposeOf;
RESP.DisposeOf;
end;
end;
I am sure I have made a mistake somewhere, that I keep overlooking...
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;
I'm trying the HMRC Developers HUB tutorials from:
https://developer.service.hmrc.gov.uk/api-documentation/docs/tutorials
I have tried two ways of the "Hello World", but keep getting:
{"code":"ACCEPT_HEADER_INVALID","message":"The accept header is missing or invalid"}
Example 1 REST Client:
procedure TForm1.btnTest_REST_ClientClick(Sender: TObject);
var
jValue: TJSONValue;
begin
RESTClient1.BaseURL := cbHMRC_Test_URLs.Text;
RESTRequest1.Execute;
jValue := RESTResponse1.JSONValue;
MemoContent.Text:= jValue.ToString;
end;
Example 2 TdHTTP:
procedure TForm1.btnTest_HTTPClick(Sender: TObject);
var
get_url: string;
resp: TMemoryStream;
begin
get_url := 'https://test-api.service.hmrc.gov.uk/hello/world';
resp := TMemoryStream.Create;
try
IdHttp1.Request.CustomHeaders.AddValue('Accept', 'application/vnd.hmrc.1.0+json');
IdHTTP1.Get(get_url, resp);
resp.Position := 0; // <-- add this!!
MemoContent.Lines.LoadFromStream(resp);
finally
resp.Free;
end;
end;
Both make the connection, but fail on the Header.
Any ideas about what I'm doing wrong?
I would suggest going with the REST components. I've used them significantly and they work quite well.
On the REST component side, your just missing the Request Accept value:
RESTRequest1.Accept := 'application/vnd.hmrc.1.0+json';
I tested your example to their hello world resource and received:
{"message":"Hello World"}
Looks like it's working.
For those of you struggling like me with how to implement the initial HMRC tutorials in Delphi, try the following.
Create a new application. I chose a Multi-Device / Blank Application option.
On the main form, add the following components:-
TRESTClient
TRESTRequest
TRESTResponse
TMemo
TButton
Add the System.JSON unit to the uses clause.
Set the Button1Click procedure as follows:-
procedure TForm1.Button1Click(Sender: TObject);
var
jValue: TJSONValue;
begin
RESTClient1.BaseURL := 'https://test-api.service.hmrc.gov.uk/hello/world';
RESTRequest1.Accept := 'application/vnd.hmrc.1.0+json';
RESTRequest1.Execute;
jValue := RESTResponse1.JSONValue;
Memo1.Text:= jValue.ToString;
end;
Run the program, click the button and voila!
I hope this helps someone
On to more curious challenges in the Delphi/Indy/TWebBrowser/Tableau saga.
I'm having a very hard time understanding why sending a GET request to the Tableau server utilizing the TIdHttp component always returns a JSON response, while the same request in a TWebBrowser control with the same auth header returns an XML response.
I slightly prefer XML - even though it is more bandwidth intensive - since we have much XML infrastructure and currently no JSON infrastructure.
When I send the request with the TIdHttp component, I'm sending it with the following params:
http.Request.CustomHeaders.Text := GetAuthHeader(FToken);
http.Request.ContentType := 'application/json';
I've also tried this:
http.Request.ContentType := 'text/xml';
http.Request.Accept := 'text/xml';
And
http.Request.ContentType := 'application/xml';
http.Request.Accept := application/xml';
And
<no settings specified for accept and contenttype, just let it use the defaults>
...
sHTML := http.Get('http://<myserver>/api/3.0/sites/' + FSiteId + '/views')
...and always receive back the response in JSON.
When I send the same request with TWebBrowser:
var
hdr,flags,targetframe,postdata,Aurl: OleVariant;
begin
AUrl := http://<myserver>/api/3.0/sites/' + FSiteId + '/views';
flags := navNoHistory+navNoReadFromCache+navNoWriteToCache;
targetframe := 1;
postdata := 1;
hdr := GetAuthHeader(FToken);
Navigate2(Aurl,flags,targetframe,postdata,hdr);
end;
...the response always comes back as XML.
Does anyone understand why this would occur? I've tried logging the raw request to see how it's being set up by TWebBrowser, but can't seem to get the raw request. Perhaps I should set up a proxy server...
TIA
Solved - had a
http.Request.ContentType := 'application/json...';
and a
http.Request.Accept := 'application/json...';
still hanging around from the form create event.
I have encountered a few sites that uses JSON for request and response
I come across two types :
1- application/x-www-form-urlencoded as request and return a response application/json content type
2- application/json content type for both request and response
in type 1 i tried changing the the response content type using
mIdHttp.Response.ContentType := 'application/json';
but using http analyzer i can see that it doesn't change and its still text/html
now i do not know if the problem is with the fact that i can't change content type or not but i do not know how to deal with json !
a few question regarding json :
1- do i have to encode json data when posting ? how ?
2- how can i parse the json response code ? how to get it ? does it need some kind of encode or special convertion ?
3- what kind of idhttp setting for json changes with each site and needs configuring ?
I understand my questions sound a bit general, but all the other questions are very specific and don't explain the basics when dealing with 'application/json' content type.
Edit 1 :
thanks to Remy Lebeau answer i was able to successfully work with type 1
but i still have trouble sending JSON request, can someone please share an working example, this is one of the sites posting information please use this for your example :
One important note : this particular site's post and request content are exactly like each other ! and it baffles me because at the site, i specify an start date and an end date then click on a folder like icon and this post is sent (the one you can see above), and the result should be links (and it is) but instead of appearing only in request content they also appear in post ! (also i'm trying to get the links but at the post the links, the thing i want, are also being sent, how can i post something i don't have !!?)
just for more clarity here is the place i fill the date and the icon i mentioned :
You cannot specify the format of the response, unless the requested resource offers an explicit input parameter or dedicated URL for that exact purpose (ie, to request a response be sent as html, xml, json, etc). Setting the TIdHTTP.Response.ContentType property is useless. It will be overwritten by the actual Content-Type header of the response.
To send JSON in a request, you must post it as a TStream, like TMemoryStream or TStringStream, and set the TIdHTTP.Request.ContentType as needed, eg:
var
ReqJson: TStringStream;
begin
ReqJson := TStringStream.Create('json content here', TEncoding.UTF8);
try
IdHTTP1.Request.ContentType := 'application/json';
IdHTTP1.Post(URL, ReqJson);
finally
ReqJson.Free;
end;
end;
To receive JSON, TIdHTTP can either
return it as a String (decoded using a server-reported charset):
var
ReqJson: TStringStream;
RespJson: String;
begin
ReqJson := TStringStream.Create('json content here', TEncoding.UTF8);
try
IdHTTP1.Request.ContentType := 'application/json';
RespJson := IdHTTP1.Post(URL, ReqJson);
finally
ReqJson.Free;
end;
// use RespJson as needed...
end;
write the raw bytes to an output TStream of your choosing:
var
ReqJson: TStringStream;
RespJson: TMemoryStream;
begin
RespJson := TMemoryStream.Create;
try
ReqJson := TStringStream.Create('json content here', TEncoding.UTF8);
try
IdHTTP1.Request.ContentType := 'application/json';
RespJson := IdHTTP1.Post(URL, ReqJson, RespJson);
finally
ReqJson.Free;
end;
RespJson.Position := 0;
// use RespJson as needed...
finally
RespJson.Free;
end;
end;
The HTTP response code is available in the TIdHTTP.Response.ResponseCode (and TIdHTTP.ResponseCode) property.