#1 2017-10-12 17:21:05

JD
Member
Registered: 2015-08-20
Posts: 118

Interface based services (HTTP GET/POST confusion)

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

#2 2017-10-12 17:52:50

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,661
Website

Re: Interface based services (HTTP GET/POST confusion)

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.

Offline

#3 2017-10-12 19:18:26

JD
Member
Registered: 2015-08-20
Posts: 118

Re: Interface based services (HTTP GET/POST confusion)

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

#4 2017-10-13 16:21:42

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,661
Website

Re: Interface based services (HTTP GET/POST confusion)

Put the parameters as a JSON object in the body of a POST request.

Offline

#5 2017-10-14 14:28:42

JD
Member
Registered: 2015-08-20
Posts: 118

Re: Interface based services (HTTP GET/POST confusion)

ab wrote:

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

#6 2017-10-14 23:42:31

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,661
Website

Re: Interface based services (HTTP GET/POST confusion)

Use the cross platform units to create your Indy client.

See the documentation.

Offline

#7 2017-10-15 13:45:17

JD
Member
Registered: 2015-08-20
Posts: 118

Re: Interface based services (HTTP GET/POST confusion)

ab wrote:

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

#8 2017-10-15 19:34:20

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,661
Website

Re: Interface based services (HTTP GET/POST confusion)

I am not sure that I understand what you mean, and where your problem is, sorry...

Offline

#9 2017-10-15 23:13:56

JD
Member
Registered: 2015-08-20
Posts: 118

Re: Interface based services (HTTP GET/POST confusion)

ab wrote:

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

#10 2017-10-16 10:33:49

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,661
Website

Re: Interface based services (HTTP GET/POST confusion)

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

Offline

#11 2017-10-17 15:30:22

JD
Member
Registered: 2015-08-20
Posts: 118

Re: Interface based services (HTTP GET/POST confusion)

ab wrote:

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

#12 2017-12-18 13:50:31

ImproSnake
Member
Registered: 2017-06-20
Posts: 30

Re: Interface based services (HTTP GET/POST confusion)

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

#13 2017-12-18 14:10:38

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,661
Website

Re: Interface based services (HTTP GET/POST confusion)

Did you try with

function TService.method(const obj: RawJSON): RawJSON;

Offline

#14 2017-12-18 14:24:44

ImproSnake
Member
Registered: 2017-06-20
Posts: 30

Re: Interface based services (HTTP GET/POST confusion)

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

#15 2017-12-18 18:01:22

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,661
Website

Re: Interface based services (HTTP GET/POST confusion)

Put a breakpoint in TServiceService.method and try to find out the exact returned information by using the step-by-step debugger.

Offline

#16 2017-12-19 09:35:55

ImproSnake
Member
Registered: 2017-06-20
Posts: 30

Re: Interface based services (HTTP GET/POST confusion)

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

Board footer

Powered by FluxBB