RTC Forums
March 31, 2017, 01:24:23 AM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Login Register  
Pages: [1]
  Print  
Author Topic: Database Access -- JSON-RPC  (Read 1503 times)
Peter M.
RTC Portal VCL License
****
Posts: 27


« on: March 10, 2012, 01:06:20 AM »

Danijel,

I really appreciated the JSON brainstorming session and the "proposed" behavior of the JSON parser in the following thread:
   http://www.realthinclient.com/sf/index.php?topic=720.msg1848#msg1848

And I want to thank jonb2 and Kevin Powick for their feedback and insightful comments.

Also, I tested the parsing speed of your RTC_JSONXMLTest.exe ... it was very fast and seemed to be accurate.
(I have over a gig of JSON GIS data ("geo spatial") so I was able to push the test app until it gave an "out of memory" error, however I do not think that was a limit of your parser but instead was a limit of the TMemo component which displays the output data.)


Now, looking at how this would be used within Database Access, please let me know if I am on the right track...

The first thing a user must do is login to the server, therefore assume I have added an RTC remote function on the server called "login" which takes 2 parameters: "username" and "password".

This "login" function tries to locate a record in the USERS table which matches that "username/password" combination.
a. If the USERS record was not found, then the server sends an error message back to the client:
       (ex: "Username/Password not found ... please try again.")
b. But if the USERS record was found then the server creates a unique "SessionID" and sends it back to the client.
   And this SessionID will be used everytime that client calls a remote function.
       (ex: "ABCDEFGH-IJKL-MNOP-QRST-UVWXYZ012345")
   

To understand what is generated by the client, the JSON-RPC 2.0 specifictation says that each JSON-RPC call should include 4 items ("members"):
   - "jsonrpc" - A mandatory string containing the JSON-RPC version number (ex: "2.0).
   - "method"  - A mandatory string identifying the name of the remote function (ex: "login")
   - "params"  - An optional set of parameters (ex: "username" and "password").
   - "id"      - A mandatory string or integer which is generated by the client each time remote function is called (ex: "3").
                 (Note-1: It is sent by the client and returned back by the server within the "result".)
                 (Note-2: It changes each time the client calls a remote function.)
                 (Note-3: It is not required if this is just a "Notification" vs. a "Request".)

But if the server send back an error, then the error would include 2 or 3 items ("members"):
  - "code"    - A mandatory integer specific for this error (ex: 12345)
  - "message" - A mandatory string containing a short description (ex: "Invalid Login")
  - "data"    - An optional string containing a longer description (ex: "Username/Password not found ... please try again.")


Therefore, it seems that the JSON for the "login" function would look like this:
Code:
  {
    "jsonrpc": "2.0",
    "method" : "login",
    "params" : { "username": "admin", "password": "12345" },
    "id"     : 3
  }


And if the login was successful, the server would send back:
Code:
  {
     "jsonrpc": "2.0",
     "result" : {"session_id": "ABCDEFGH-IJKL-MNOP-QRST-UVWXYZ012345"},
     "id"     : 3
  }


...But if unsuccessful, then:
 
Code:
{
     "jsonrpc": "2.0",
     "error"  : { "code": 12345, "message": "Invalid Login", "data": "Username/Password not found ... please try again." },
     "id"     : 3
  }



Questions :
  (Assume RtcClientModule.DataFormat = "fmt_JSON".)

1. When adding parameters for the "login" function, am I expected/required to use the words: "method" and "params"?

2. Will RTC SDK automatically create and include the "id"?

3. When receiving the JSON result, will I be expected/required to use the words: "result", "error", "code", "message" and "data" ?

Thanks,
-Peter


Logged
Danijel Tkalcec
Administrator
*****
Posts: 1477


« Reply #1 on: March 10, 2012, 01:54:34 AM »

Hi Peter,

What you are looking at is JSON-RPC specification, which is one of the less commonly used formats when using JSON over HTTP. A more common use is JSON + REST, which is also what I was thinking about adding as an additional format for use with RTC remote functions.

Since TRtcServerModule and TRtcClientModule don't have support for the JSON format, I can't answer your questions yet, but one of my goals with this integration would be to make the use of a JSON format as easy as possible. For the Server side, it would simply mean enabling the "fmt_JSON" parameter and all remote functions would become accessible by a 3rd-party over JSON, the same way they can now be accessed over the RTC and XML-RPC formats.

When communicating with 3rd-party Servers, or in case you need to provide a specific interface in your Server for 3rd-party clients, you might need to use JSON-specific words, but I would like to keep this requirement to the minimum.

Please note that this is all just the general idea, nothing is "carved in stone" yet. I want to make sure that JSON parser and generator are fully functional, before I start integrating JSON-RPC and/or JSON+REST into TRtcClientModule and TRtcServerModule components. Until then, you can use the TRtcValue class for parsing and generating JSON, with a TRtcDataRequest (client) and TRtcDataProvider (server) components for communication.

PS. "JSON-RPC" v1.0 is currently the only "official" JSON-RPC standard, with v2.0 being in "proposal" state since 2009, with no visible movement. Googling for "JSON-RPC" and comparing the number of results to "JSON+REST" also goes in favor of using "JSON+REST" (62.200.000 hits for JSON+REST, but only 814.000 for JSON-RPC). 

Best Regards,
Danijel Tkalcec
Logged
Peter M.
RTC Portal VCL License
****
Posts: 27


« Reply #2 on: March 10, 2012, 02:50:23 AM »

Danijel,

Thanks for the clarification between RPC vs REST, and also for showing that JSON-RPC 2.0 is *not* the standard.

... and yes, I do realize nothing is "carved in stone" yet  Smiley 

(However, I really appreciate that you give all of us the opportunity to discuss these items with you during your development process.)

-Peter
Logged
Danijel Tkalcec
Administrator
*****
Posts: 1477


« Reply #3 on: March 10, 2012, 09:27:42 AM »

Here is how RTC types will be mapped to JSON types when generating JSON from TRtcValue objects ...

rtc_Null (isNull) => null

rtc_Boolean (asBoolean) => true/false

rtc_Integer (asInteger) => JSON Number
rtc_Cardinal (asCardinal) => JSON Number
rtc_LargeInt (asLargeInt) => JSON NUmber
rtc_Float (asFloat) => JSON Number
rtc_Currency (asCurrency) => JSON Number
rtc_OID (asOID) => JSON Number

rtc_Text (asText) => JSON String
rtc_String (asString) => JSON String
rtc_WideString (asWideString) => JSON String
rtc_Variable (asVariable) => JSON String

rtc_Exception (asException) => JSON String,
with the '\/error\/' prefix if RTC_JSON_GenTypedException=True;

rtc_DateTime (asDateTime)  => JSON String,
in "yyyy-mm-ddThh:mm:ss.ms" format if RTC_JSON_GenTypedDateTime=False,
or in a "\/Date(milliseconds-since-1970-1-1)\/" format if RTC_JSON_GenTypedDateTime=True;

rtc_ByteStream (newByteStream/asByteStream) => JSON String with base64-encoded content from the Stream,
with a '\/base64\/' prefix if RTC_JSON_GenTypesByteStream=True (without prefix otherwise);

rtc_Array (newArray/asArray) => JSON Array,
with all RTC array items as JSON Array elements

rtc_Record (newRecord/asRecord) => JSON Object,
with all RTC record fields as JSON Object members

rtc_Function (newFunction/asFunction) => JSON Object with all Function parameters as JSON Object members,
with FunctionName as "\/method" member if RTC_JSON_GenTypedFunctions=True;

rtc_DataSet (newDataSet/asDataSet) => JSON Object containing 2 JSON Arrays ("\/dsfields" and "\/dsrows").
The "\/dsfields" JSON Array containing all Fields from a rtc_DataSet as Object elements (with "Name", "Type", "Size" and "Req" members), and a 2-dimensional JSON Array "\/dsrows", containg all the rows from the rtc_DataSet.

Best Regards,
Danijel Tkalcec
Logged
Danijel Tkalcec
Administrator
*****
Posts: 1477


« Reply #4 on: March 10, 2012, 01:03:44 PM »

And this is how the JSON parser is currently converting JSON into TRtcValue objects ...

null => rtc_Null (isNull=True)

true / false => rtc_Boolean (asBoolean)

JSON Number with no decimal point =>
  rtc_LargeInt (asLargeInt) if the number fits into a 64bit integer variable.
  rtc_String (asString) if the number can NOT be converted into 64bit integer.

JSON Number with a decimal point (".") or exponent ("E"/"e") =>  
  rtc_Float (asFloat) if JSON Number has less than 20 characters.
  rtc_String (asString) if JSON Number has 20 or more characters.

JSON String =>
  rtc_Exception (asException): if the String starts with '\/error\/', and RTC_JSON_ParseTypedException=True;
  rtc_DateTime (asDateTime): if the String starts with '\/Date(', and RTC_JSON_ParseTypedDateTime=True;
  rtc_ByteStream (asByteStream) if the String starts with '\/base64\/', and RTC_JSON_ParseTypesByteStream=True;
  rtc_Text (asText) in all other cases (no type conversion applied).

JSON Array => rtc_Array (asArray)

JSON Object =>
  rtc_DataSet (asDataSet) if the 1st Object member is "\/dsfields" or "\/dsrows",
        and RTC_JSON_ParseTypedDataSet=True;
  rtc_Function (asFunction) if the 1st Object member is "\/method",
        and RTC_JSON_GenTypedFunctions=True;
  rtc_Record (asRecord) in all other cases.

All comments, suggestions, discussions and questions are welcome.

Best Regards,
Danijel Tkalcec
Logged
Danijel Tkalcec
Administrator
*****
Posts: 1477


« Reply #5 on: March 12, 2012, 11:16:53 AM »

Using TRtcDataRequest (client) and TRtcDataProvider (server) components, you can already communicate with 3rd-party clients and servers using JSON, by utilizing the TRtcValue class and the new "FromJSON" constructor and "ToJSON" method.

For example, if you want to generate the JSON below ...
Code:
{
    "jsonrpc": "2.0",
    "method" : "login",
    "params" : { "username": "admin", "password": "12345" },
    "id"     : 3
}
... you can do something like this with the RTC SDK:
Code:
var
  rtcobj:TRtcValue;
  json:AnsiString;
begin

// Create a new TRtcValue object ...
rtcobj:=TRtcValue.Create;

// Fill the object with data ...
with rtcobj.newRecord do // {
  begin
  asText['jsonrpc']:='2.0';   //  "jsonrpc": "2.0",
  asText['method']:='login'; //  "method" : "login",
  with newRecord('params') do    //  "params" : {
    begin
    asText['username']:= 'admin'; // "username": "admin",
    asText['password']:= '12345'; // "password": "12345"
    // More method parameters can be assigned here ...
    end;   // },
  asInteger['id']:=3;   //  "id"     : 3
  end;  // }

// Generate JSON from prepared object ...
json := rtcobj.toJSON;

// Now, you have the data formatted in JSON, ready for transport
end;

RTC SDK will NOT be using any other encoding than JSON when generating JSON from TRtcValue objects, so the receiver won't have to use anything else than JSON to decode the data. The content generated by the "TRtcValue.ToJSON" method will still be safe for use with UTF-8 decoders, because the complete string won't contain anything that an UTF-8 encoder could miss-interpret, but there won't be any need to use UTF-8 or any other decoding.

The situation might be different when working with a 3rd-party Client or Server working with JSON (see the post below for more info).

Best Regards,
Danijel Tkalcec
Logged
Danijel Tkalcec
Administrator
*****
Posts: 1477


« Reply #6 on: March 12, 2012, 11:40:11 AM »

Provided you have a String variable "json" which contains JSON data, and you are expecting a JSON-RPC 2.0 error in an object like this ...
Code:
{
     "jsonrpc": "2.0",
     "error"  : { "code": 12345, "message": "Invalid Login", "data": "Username/Password not found ... please try again." },
     "id"     : 3
}
... or a JSON-RPC 2.0 result in an Object like this ...
Code:
{
     "jsonrpc": "2.0",
     "result" : {"session_id": "ABCDEFGH-IJKL-MNOP-QRST-UVWXYZ012345"},
     "id"     : 3
}
... you can get a "rtcobj:TRtcValue" object from that "json:String" variable by calling ...
Code:
rtcobj:=TRtcValue.FromJSON(json);
... after that, you will be able to access all JSON data through TRtcValue objects, using something like ...
Code:
if rtcobj.isType=rtc_Record then // we have a Record (JSON Object)
  with rtcobj.asRecord do // {
    begin
    MyJSON_Ver := asText['jsonrpc']; //  "jsonrpc": "2.0",
    if isType['error']=rtc_Record then // we have a Record (JSON Object) called "error"
      with asRecord['error'] do // "error" : {
        begin
        MyErrorCode := asInteger['code']; // "code": 12345,
        MyErrorMsg := asText['message']; // "message": "Invalid Login"
        MyErrorData := asText['data']; // "data": "Username/Password not found ... please try again."
        end; // }
    if isType['params']=rtc_Record then // we have a Record (JSON Object) called "params"
      with asRecord['params'] do // "params : {
        begin
        MySessionID := asText['session_id']; // "session_id": "ABCDEFGH-IJKL-MNOP-QRST-UVWXYZ012345"
        // You can access all other "params" members here ...
        end;  // }
    MyJSON_ID:= asInteger['code']; // "id" : 3
    end; // }

If you receive JSON data from a source which is NOT encoding all Unicode characters with "\u" (JSON Unicode character encoding), but instead uses UTF-8 encoding, you can get a Unicode String from that UTF-8 encoded String by using the "utf8decode()" function from the "rtcInfo.pas" unit. Please note that you should NOT use "utf8decode" if the content received is not in UTF-8 format, or the decode function will return an empty string.

Best Regards,
Danijel Tkalcec
Logged
Danijel Tkalcec
Administrator
*****
Posts: 1477


« Reply #7 on: February 16, 2017, 10:20:14 AM »

I'm working on a RTC SDK update now to add support for writing and sending remote functions using the JSON-RPC 2.0 format with TRtcClientModule and TRtcServerModule components.

To make the use of different data formats transparent, the same rules which already apply to remote functions using XML-RPC will be used when calling and writing remote functions using the JSON-RPC 2.0 format, with the main difference being the value used for the "DataFormat" property. In other words ...

1.a) You will NOT be using the "method" parameter to define the name of the remote function being called, but will use TRtcClientModule's "Prepare" method or the "Data.newFunction" property, the same way as using the RTC and XML-RPC formats.

1.b) When preparing remote function calls using named parameters, you will be assigning parameters directly by name (NOT through the "params" property), just like you would when sending named parameters using the RTC or XML-RPC formats.

1.c) If you need to call a 3rd-party Service and have to pass your parameters as an array (instead of using names for each parameter), you will be preparing your parameters by creating an array named "params", just like it is now the case when making XML-RPC calls by using an array for parameters.

2.) TRtcClientModule component will automatically create and include the "id" when using the JSON-RPC 2.0 format and the TRtcServerModule component will automatically return the same ID back.

3.a) As already the case when using the built-in XML-RPC format, when using JSON-RPC 2.0 you will also NOT be using the word "result" when preparing a Result on the Server or processing a Result received on your Client. On the Serve-side, anything you prepare using the Result parameter (passed on to the event) will be sent as the "result" JSON object to the Client. And on the Client-side, anything recieved in the "result" JSON object from the Server will be made accessible directly through the standard Result parameter.

3.b) If an exception is raised while executing a JSON-RPC 2.0 request on the Server, TRtcServerModule component will be sending a response with the "error" JSON object back to the Client containing the Exception Message. And if the Client receives the "error" object in the JSON-RPC 2.0 response, all the error information will be accessible as a single string using the "Result.asException" property, as is already the case with RTC and XML-RPC formats, following the same rules as for the XML-RPC format for "code" and "message" (#code:message).

3.c) Since JSON-RPC 2.0 also allows the use of an optional "data" parameter in its "error" object, in order to keep the use of different formats with the TRtcClientModule component transparent and to simplify accessing error information, I will be adding new "asErrorCode", "asErrorMessage" and "asErrorData" properties to Rtc Value objects. All information about these new properties will be stored in the TRtcExceptionValue object and handled transparently. The asException property will be working as before, giving you access to a combination of ErrorCode and ErrorMessage in a single string (#ErrorCode:ErrorMessage), with the ErrorData parameter accessible separately.

Best Regards,
Danijel Tkalcec
Logged
Danijel Tkalcec
Administrator
*****
Posts: 1477


« Reply #8 on: March 02, 2017, 04:28:58 PM »

RealThinClient SDK v7.107 (just released) now extends RTC Remote Functions (TRtcClientModule and TRtcServerModule) to include built-in support for using JSON-RPC 1.0, JSON-RPC 2.0 and a number of different requests modes for using JSON,  XML-RPC and RTC in combination with REST (remote function name and/or parameters in the URI).

Best Regards,
Danijel Tkalcec
Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.059 seconds with 17 queries.