Teaser : Easy REST with kbmMW and Delphi

kbmMW has for many years supported REST, but it did require some manual work in marshalling data in and out. Further the URL’s allowed for calling kbmMW’s REST interface had to be formatted fairly strictly.

Using the new kbmMW smart service which is being developed, you can extremely easily publish functions to be consumed by the public via REST via any URL you like.

A kbmMW smart service which have some functions available for calling from smart clients (a new kbmMW client type) and REST clients, could look like this:

type

 // This service is known to smart clients as SMARTDEMO
 // Methods registered for REST access will, if they
 // provide a relative path, be sub pathed under /myserver
 [kbmMW_Service('SMARTDEMO')]
 [kbmMW_Rest('path:/myserver')]
 TkbmMWCustomService2 = class(TkbmMWCustomSmartService)
 public

 // This method can be called both from REST and
 // from smart clients.
 // Its called from REST clients like this:
 // http://../helloworld
 [kbmMW_Method('HelloWorld')]
 [kbmMW_Rest('method:get, path: "/helloworld"')]
 function HelloWorld:string;

 // This method can be called both from REST and
 // from smart clients.
 // Its called from REST clients like this:
 // http://../myserver/myechostring/somestring
 // or
 // http://../myserver/echostring/somestring
 // somestring (can be anything) will be echoed back.
 // The argument AString will automatically receive
 // the value provided as somestring.
 [kbmMW_Method('EchoString')]
 [kbmMW_Rest(
 'method:get, '+
 'path: [ "echostring/{AString}","myechostring/{AString}" ]')]
 function EchoString(
  [kbmMW_Rest('value: "{AString}"')] const AString:string):string;

// This method is only available from REST calls, not
 // from smart clients. (kbmMW_Method attribute is missing)
 // It can be called from a browser like this:
 // http://.../someabspath/addnumbers?arg1=10&arg2=20
 // The functions arguments are automatically populated
 // with the arguments.
 [kbmMW_Rest('method:get, path: "/someabspath/addnumbers"')]
 function AddNumbers(
  [kbmMW_Rest('value: "$arg1", required: true')] const AValue1:integer;
  [kbmMW_Rest('value: "$arg2", required: true')] const AValue2:integer;
  [kbmMW_Arg(mwatRemoteLocation)] const ARemoteLocation:string):integer;

// This method gets and owns a TPerson instamce, which it must
 // itself free. The method is not available for REST calls.
 [kbmMW_Method]
 function StorePerson(
  [kbmMW_Arg(mwatOwnedValue)] const APerson:TPerson):integer;

 // This method is only available from smart clients.
 // It receives a TPerson instamce which is owned by the
 // caller. When called from a smart client, the kbmMW framework
 // is the owner and will automatically free it.
 [kbmMW_Method]
 function StorePerson2(const APerson:TPerson):integer;

 // This method is only available from smart clients.
 // It receives a IPerson2 interface.
 [kbmMW_Method]
 function StorePerson3(const APerson:IPerson2):integer;

 // This method can be called both from REST and
 // from smart clients.
 // The method returns a TPerson instamce which is
 // automatically freed by the kbmMW framework after marshalling.
 // (Its default to free a returned object)
 [kbmMW_Rest('method:get, path: "getperson/{id}", freeResult:true')]
 [kbmMW_Method(true)]
 function GetPerson([kbmMW_Rest('value:{id}')]id:string):TPerson;
end;

// Functions published by the service.
//------------------------------------

function TkbmMWCustomService2.HelloWorld:string;
begin
 Result:='Hello world';
end;

function TkbmMWCustomService2.EchoString(const AString:string):string;
begin
 Result:=AString;
end;

function TkbmMWCustomService2.ReverseString(
 const AString:string;
 const AClientIdentity:TkbmMWClientIdentity):string;
begin
 Result:=StrUtils.ReverseString(AString);
end;

function TkbmMWCustomService2.AddNumbers(
 const AValue1,AValue2:integer;
 const ARemoteLocation:string):integer;
begin
 Result:=AValue1+AValue2;

// In ARemoveLocation the reported remote location
 // for the client is found.
end;

function TkbmMWCustomService2.StorePerson(
 const APerson:TPerson):integer;
begin
 // The TPerson instance is owned by this function,
 // and must be manually freed.
 APerson.Free;
 Result:=0;
end;

function TkbmMWCustomService2.StorePerson2(
 const APerson:TPerson):integer;
begin
 // The TPerson instance is automatically freed
 // upon exit of this function.
 Result:=0;
end;

function TkbmMWCustomService2.StorePerson3(
 const APerson:IPerson2):integer;
begin
 // The lifetime of IPerson2 depends on reference counting.
 Result:=0;
end;

function TkbmMWCustomService2.GetPerson(id:string):TPerson;
begin
 Result:=TPerson.Create;
 Result.Name:='Sofie Mogensen';
 Result.Age:=87;
end;

initialization
 TkbmMWRTTI.EnableRTTI(TkbmMWCustomService2);

Examples on REST calls:

GET http://localhost/myserver/getperson/1

{“person”:{“Name”:”Sofie Mogensen”,”Address”:””,”Age”:87}}

GET http://localhost/someabspath/addnumbers?arg1=10&arg2=20

30

Its dead easy to create the server. Just have a central form/datamodule with a kbmMWServer instance, a transport with the new REST streamformat specified, and configured to listen on port 80, and add all the smart service units you want (like the above). Then only the following 2 lines are needed:

kbmMWServer1.AutoRegisterServices;
kbmMWServer1.Active:=true;

and you are running your REST and smart client server!

As usual all this can be combined with traditional services and clients. While REST calls results in objects automatically being streamed and unstreamed as JSON, smart clients will automatically stream and unstream object and interface instances in messagepack format, which is now also supported.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s