Reputation: 724
I am trying to access a REST server that has an endpoint that uses the GET verb while it also requires json data to be sent in the body
I am trying to accomplish that with an TIdHttp class, using the Get method.
Right now I create a TStringStream with the json data, and then assign that stream to the Request.Source property of the TIdHttp object.
The server however responds with an error code indicating it did not receive the body.
How do you send a body with GET request using TIdHttp?
Upvotes: 1
Views: 4192
Reputation: 23036
As Remy suggests, you could hack an 'accessor' class to contrive to access this method via a type-coerced call.
Or, you could simply sub-class TIdHttp
and add an appropriate, new Get() convenience method, following the patterns established for all of the other such methods.
e.g. something like:
interface
type
TOurHttp = class(TIdHttp)
public
procedure Get(aUrl: String; aRequestBody, aResponseContent: TStream);
end;
implementation
procedure TOurHttp.Get(aUrl: String; aRequestBody, aResponseContent: TStream);
begin
DoRequest(Id_HttpMethodGet, aUrl, aRequestBody, aResponseContent, []);
end;
This is what we did at a company I worked at previously, building integration/middleware software, as there were many examples where the provided Indy convenience methods (at the time) did not cover all of our use cases, not just GET, especially when it came to REST Api's. Note that the above is not our actual code from that time, I no longer have access to that.
Instead of using the TIdHttp
class directly we used TOurHttp
(not the real name), bestowed with numerous additional convenience methods useful to us. We also found we had to make changes to and extend aspects of other things such as the IgnoreReplies
behaviour, to accommodate various 'rogue' REST Api server behaviours (not all of which were HTTP standards compliant) which again you would not find in the majority of use cases.
Of course, instead of fixing your client behaviour, you could try asking the provider of the Api or HTTP server to change their end. Good luck with that. ;)
The HTTP protocol specification does allow for a GET request to submit a request body, but the vast majority of implementations do not (or simply ignore any such content). This is perhaps because historically the vast majority of implementations have targeted - or been driven by - browser behaviours, combined with the fact that the HTTP specification has unfortunately allowed some "wriggle room" in this area.
This has resulted in where we are today when, at time of writing (January 2019) the relevant part of the HTTP spec RFC7231 has this to say about content in a GET request:
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
i.e. request content is allowed, since it is not explicitly forbidden and its use is acknowledged as possible and sometimes expected (and only sometimes rejected). But you cannot rely on a server adopting any particular interpretation (or even acceptance) of such a request.
That of course doesn't help a client dealing with a particular server that has decided on its particular interpretation. By conceding to bad practice in this way this iteration of the HTTP specification is less than useful; it simply describes the variance in practice rather than providing a specification against which an implementation may be tested for compliance/accuracy.
Upvotes: 4
Reputation: 596537
Assigning the TIdHTTP.Request.Source
property will not work because the Source
will get replaced with nil
when TIdHTTP.Get()
calls TIdHTTP.DoRequest()
internally passing nil
for its ASource
parameter.
So, to do what you are asking, you will have to call DoRequest()
directly. However, it is a protected
method, so you will have to use an accessor class to reach it.
For example:
type
TIdHTTPAccess = class(TIdHTTP)
end;
var
QueryData, ReplyData: TStream;
begin
QueryData := TStringStream.Create('... json data here...', TEncoding.UTF8);
try
IdHTTP1.Request.ContentType := 'application/json';
IdHTTP1.Request.CharSet := 'utf-8';
//Result := IdHTTP1.Get('http://...');
ReplyData := TMemoryStream.Create;
try
TIdHTTPAccess(IdHTTP1).DoRequest(Id_HTTPMethodGet, 'http://...', QueryData, ReplyData, []);
ReplyData.Position := 0;
Result := ReadStringAsCharset(ReplyData, IdHTTP1.Response.Charset);
finally
ReplyData.Free;
end;
finally
QueryData.Free;
end;
end;
Upvotes: 4