You are not logged in.
Hi there,
I'm a little confused about how to perform HTTP GET/POST from Delphi/Lazarus clients. I've been going through the documentation but it is still not very clear to me. I am using ServiceContext.Request.Method to distinguish between the two commands in an interface method. The REST method is as shown below (I tried to keep it as short and readable as possible to not break forum rules):
function TRESTMethods.Country(aSchema: string; const aID: string; aDTO: RawJSON): RawJSON;
var
Res: ISQLDBRows;
aObj: TSQLCountry;
begin
case ServiceContext.Request.Method of
mGET:
begin
Res := aServer.fDbProps.Execute(Format('select id, name from %s.country where id=?', [aSchema]), [aID])
Result := Res.FetchAllAsJSON(True);
end;
mPOST:
begin
aObj := TSQLCountry.Create;
try
ObjectLoadJSON(aObj, aDTO); // Load JSON into object
Res := aServer.fDbProps.Execute(Format('INSERT INTO %s.country (name) VALUES (?) RETURNING country_id', [aSchema]), [aObj.Name]);
while Res.Step do
Result := Format('{"ID": "%s"}', [VariantSaveJSON(Res['country_id'], twNone)]);
finally
aObj.Free;
end;
end;
end;
end;
The problem is that when I call it from a browser or from a REST client using
http://localhost:888/service/myapi/country?aschema=public
the code under mGET is executed as expected.
But when I try to call it from a Lazarus client using
if Client.Services['MyAPI'].Get(I) then
Memo1.Lines.Add(I.Country('public', '', ''));
it tries to execute the mPOST portion of the code and fails obviously.
Questions
a) how do I send GET/POST and other HTTP commands like PUT & DELETE from a Lazarus client to my REST method above
b) I would prefer to send POST and PUT information via the message body instead of the URI. How can I do this from a Lazarus client?
Thanks a lot for your kind assistance
JD
Offline
An interface-based service does not follow the GET/POST/DELETE verbs.
It may be executed via a GET (e.g. from a browser and URI-encoded parameters), or a POST (sending the parameters as a JSON Object or Array).
If you want to handle REST/HTTP verbs, use method-based services.
Please check the documentation about this.
It is clearly explained.
Online
Thanks for your reply ab. I've modified the method and removed its dependence on REST/HTTP verbs. Now it works as it should.
However since URI size is limited to 255 characters, can you please show me how I can send parameters as a JSON object or array in the request body. 16.8.6.1.1.4? Sending a JSON object in the documentation does not provide an example.
Thanks a lot,
JD
Last edited by JD (2017-10-12 19:18:57)
Offline
Put the parameters as a JSON object in the body of a POST request.
Hi there ab,
That is just what I need to know. My interface method signature is
function TRESTMethods.Country(aSchema: string; const aID: string; aDTO: RawJSON): RawJSON;
In body of the function, if aDTO is an empty string, it is treated as a GET request and if aDTO is not empty, it is treated as a POST/PUT request. Using the contents of aDTO has helped me remove the dependence on HTTP verbs. I know how to send a POST request in the URI on one line when the function is called on the client side like this
Client.Country('public', ' ', '{"id":"0","name","Barbados"}');
and it works!
I want to know how to add the JSON object {"id":"0","name","Barbados"} to the request body so that I don't end up having long URIs.
For example in Indy 10, I can do this https://mikejustin.wordpress.com/2015/0 … ttps-post/ or this https://stackoverflow.com/questions/422 … https-post
Indy 10's HTTP.Post is an overloaded function/procedure that can be used to send requests and receive responses. What is the equivalent of this in mORMot for interface based services? I've been looking at HTTPClient but I still have not found it.
Thanks a lot,
JD
Last edited by JD (2017-10-14 14:30:48)
Offline
Use the cross platform units to create your Indy client.
See the documentation.
I already have a working client. I am porting a large application to mORMot. I'm not using the ORM part of mORMot. I've decided to start with rewriting the existing Indy server using mORMot interface based services and that is moving along fine. On the client side, there is a GUI and a transport layer. The transport layer is Indy based and is what sends and receives data from the server. I don't intend to change the client side GUI since it is agnostic (it does not know how data is sent/received). It is the transport layer that I'm trying to rewrite using mORMot. At present, the following have been implemented client-side
a) sending requests (querying PostgreSQL tables) to the new mORMot server
b) modifying data in the remote tables (the catch here is that the data is sent in the URI respecting the limits of URI length)
The mORMot documentation says the following
Note that there is a known size limitation when passing some data with the URI over HTTP. Official RFC
2616 standard advices to limit the URI size to 255 characters, whereas in practice, it sounds safe to
transmit up to 2048 characters within the URI. If you want to get rid of this limitation, just use the
default transmission of a JSON array as request body.
This is the catch ..... just use the default transmission of a JSON array as request body.
My BIG TO-DO
- some requests are to be made on some large tables. SELECT * will waste bandwidth and will be slow. Selecting ONLY the needed columns is the best strategy but the URI will be too long. So I HAVE to send the SQL via the request body like I've already done with Indy.
The problem is I do not know how to read/write to the URI request body, and I've not seen an example of it yet though I'm still searching Section 16.8.6.1.1. REST mode in the documentation is not clear to me as to how it can be done
The phrase request body occurs only 2 times in the mORMot documentation. The first one is cited above while the second one is
procedure ProcessRequest; virtual;
Method triggered to calculate the response
- expect fRequestHeaders, fRequestMethod, fRequestBody and fRequestURL properties as input
- update fResponseHeaders and fResponseContent properties as output
Conclusion
I am hoping to use solely a service interface based mORMot server. Is it possible to pass parameters to the interfaces via the request body? If so how do I do it? Or must I mix service methods with service interfaces and then use the Ctxt parameter of service methods to access the message body?
Thanks a lot,
JD
Offline
I am not sure that I understand what you mean, and where your problem is, sorry...
Instead of sending JSON parameters in the URI to an interface based mORMot server, I want to send it in the message body. I essence, how do you do the following in a mORMot client (taken from mORMot documentation section 16.8.6.1.1.4. Sending a JSON object)?
POST /root/Calculator.Add
(...)
[1,2]
That POST sends [1,2] in the message body. How? I would love to see sample code.
Thanks
JD
Offline
How?
It will depend on which library you are using for the client...
Using SynCrtSock.pas it is as simple as calling the Post() method.
See e.g. https://stackoverflow.com/a/22078557/458259
Online
How?
It will depend on which library you are using for the client...Using SynCrtSock.pas it is as simple as calling the Post() method.
See e.g. https://stackoverflow.com/a/22078557/458259
Eureka! That's it. Thanks a lot ab. That was what I was looking for.
JD
Offline
Hello, i have a similiar Problem, but the other way around.
I call Interface based services from an ajax client.
Now i want to POST an object to the server.
I do this with a normal HTTP Post call.
The Mormot Server logs that i called the Method and he even log the submitted data.
But i dont know how to access the data in the interface based method.
i.e.
function TService.method(obj: RawJSON): RawJSON;
begin
result:=obj;
end;
But the Result is always empty. Why i dont get the submitted Data in obj.
The call look like this :
http://localhost:8080/API/Service.method?session_signature=7393368f69f0c125a13c22bc
and the Payload :
{key1: val1, key2: val2, key3: val3,... and so on}
How can i access this payload on the mormot Server Side in the Interface based Service.
I know that you just can stringify the object and send it via URI, but since the URI is restricted in the length/size this isn't a suitable option.
Maybe you can point me in the right direction.
Greetings
Last edited by ImproSnake (2017-12-18 14:11:01)
Offline
Hi Ab,
unfortunately this change doesn't make the thing work.
Examplecode on the ServerSide
Interface
Unit interfaceService;
interface
uses
SynCommons,
mORMot;
type
ITickets = interface(IInvokable)
['{64DC19BA-673A-4D5F-B1D3-36629063563B}']
function method(const obj : RawJSON) : RawJSON;
end;
implementation
initialization
// so that we could use directly ISession instead of TypeInfo(ISession)
TInterfaceFactory.RegisterInterfaces([TypeInfo(IService)]);
end.
And the implementation in the Server
function TServiceService.method(const objbj: RawJSON): RawJSON;
begin
result := obj;
end;
And on the client side i do something like
public saveTicket(obj: any): void {
let uri: string = '';
uri = this.apiRoot + '/Tickets.saveTicket';
uri = this.authService.SessionSign(uri);
this.http.post(this.server + '/' + uri, obj).subscribe((response)=>{
console.log(response);
});
}
The uri with signature session boils down to something like http://localhost:8080/API/Service.method?session_signature=7393368f69f0c125a13c22bc
Where obj looks like
{ key:1 val1, key2:val2,key3:val3}
The Server lock the call and this params, that is why i suppose he got the data successfully, but i dont know how to access this data in the interface based method.
The result i get in my Browser is always [null].
Maybe this helps furhtermore
Offline
Hi ab,
Thank you for your help, i found the solution in this forum post : https://synopse.info/forum/viewtopic.php?id=3621
I defined the interval based header with the wrong parameters. And theefore mormort done what it was intended to do, it ignored the unknow ones. All fine, i just missunderstodd something.
Offline