2017-09-24 19:40:40 +02:00
unit MVCFramework. JSONRPC;
interface
uses
2017-09-28 00:14:34 +02:00
System. Classes, Data. DB, System. SysUtils,
2017-09-26 01:02:09 +02:00
jsondataobjects, MVCFramework, MVCFramework. Commons, System. Rtti,
2017-10-10 12:19:46 +02:00
System. Generics. Collections, MVCFramework. Serializer. Commons,
MVCFramework. Serializer. JsonDataObjects;
2017-09-24 19:40:40 +02:00
const
JSONRPC_VERSION = '2.0' ;
JSONRPC_HEADER = 'jsonrpc' ;
JSONRPC_METHOD = 'method' ;
JSONRPC_PARAMS = 'params' ;
JSONRPC_ID = 'id' ;
JSONRPC_RESULT = 'result' ;
JSONRPC_ERROR = 'error' ;
JSONRPC_CODE = 'code' ;
JSONRPC_MESSAGE = 'message' ;
JSONRPC_DATA = 'data' ;
type
2017-09-26 01:02:09 +02:00
IMVCJSONRPCMessage = interface
[ '{73B8D463-75E1-404B-8437-EF4B3C950D2F}' ]
function AsJSONRPCMessage: string ;
end ;
TMVCJSONRPCMessage = class abstract( TInterfacedObject, IMVCJSONRPCMessage)
2017-09-24 19:40:40 +02:00
private
fJSON: TJsonObject;
protected
2017-09-26 01:02:09 +02:00
class procedure CheckVersion( const aJSON: TJsonObject) ;
class procedure CheckMethod( const aJSON: TJsonObject) ;
class procedure CheckID( const aJSON: TJsonObject; out aIsNotification: Boolean ) ;
constructor Create; overload ;
procedure Build( const aJSON: TJsonObject) ; virtual ; abstract ;
{ IMVCJSONRPCMessage }
function AsJSONRPCMessage: string ;
public
function AsJSON: TJsonObject; virtual ;
end ;
TJSONRPCMessage = class( TObject)
private
FID: TValue;
procedure SetID( const Value: TValue) ;
function GetJSONString: string ;
protected
2017-09-28 00:14:34 +02:00
procedure SetJsonString( const Value: string ) ; virtual ;
2017-09-26 01:02:09 +02:00
function GetJSON: TJsonObject; virtual ;
2017-09-28 00:14:34 +02:00
procedure SetJSON( const Value: TJsonObject) ; virtual ; abstract ;
2017-09-26 01:02:09 +02:00
public
constructor Create; virtual ;
2017-09-28 00:14:34 +02:00
property AsJSON: TJsonObject read GetJSON write SetJSON;
property AsJSONString: string read GetJSONString write SetJsonString;
2017-10-10 12:19:46 +02:00
property RequestID: TValue read FID write SetID;
2017-09-26 01:02:09 +02:00
end ;
{$SCOPEDENUMS ON}
TJSONRPCRequestType = ( Request, Notification) ;
TJSONRPCRequest = class( TJSONRPCMessage)
2017-09-28 00:14:34 +02:00
private
type
TJSONRPCRequestParams = TList< TValue> ;
2017-09-26 01:02:09 +02:00
private
FParams: TJSONRPCRequestParams;
FMethod: string ;
procedure SetMethod( const Value: string ) ;
function GetRequestType: TJSONRPCRequestType;
protected
function GetJSON: TJsonObject; override ;
2017-09-28 00:14:34 +02:00
procedure SetJSON( const JSON: TJsonObject) ; override ;
2017-09-24 19:40:40 +02:00
public
2017-09-26 01:02:09 +02:00
constructor Create; override ;
destructor Destroy; override ;
property Method: string read FMethod write SetMethod;
property Params: TJSONRPCRequestParams read FParams;
property RequestType: TJSONRPCRequestType read GetRequestType;
2017-09-24 19:40:40 +02:00
end ;
2017-09-28 00:14:34 +02:00
TJSONRPCResponse = class( TJSONRPCMessage)
2017-09-24 19:40:40 +02:00
private
2017-09-28 00:14:34 +02:00
type
TJSONRPCResponseError = class
private
FCode: Integer ;
FMessage: string ;
procedure SetCode( const Value: Integer ) ;
procedure SetMessage( const Value: string ) ;
public
property Code: Integer read FCode write SetCode;
property ErrMessage: string read FMessage write SetMessage;
end ;
2017-09-26 01:02:09 +02:00
private
FResult: TValue;
FError: TJSONRPCResponseError;
procedure SetResult( const Value: TValue) ;
procedure SetError( const Value: TJSONRPCResponseError) ;
2017-09-24 19:40:40 +02:00
protected
2017-09-26 01:02:09 +02:00
function GetJSON: TJsonObject; override ;
2017-09-28 00:14:34 +02:00
procedure SetJSON( const JSON: TJsonObject) ; override ;
2017-09-24 19:40:40 +02:00
public
2017-09-26 01:02:09 +02:00
constructor Create; override ;
2017-09-24 19:40:40 +02:00
destructor Destroy; override ;
2017-09-26 01:02:09 +02:00
property Result : TValue read FResult write SetResult;
property Error: TJSONRPCResponseError read FError write SetError;
2017-09-24 19:40:40 +02:00
end ;
EMVCJSONRPCInvalidVersion = class( Exception)
end ;
EMVCJSONRPCException = class( Exception)
2017-09-26 01:02:09 +02:00
end ;
EMVCJSONRPCErrorResponse = class abstract( Exception)
2017-09-24 19:40:40 +02:00
private
FJSONRPCErrorCode: Integer ;
public
property JSONRPCErrorCode: Integer read FJSONRPCErrorCode;
end ;
2017-09-26 01:02:09 +02:00
EMVCJSONRPCParseError = class( EMVCJSONRPCErrorResponse)
2017-09-24 19:40:40 +02:00
public
constructor Create;
end ;
2017-09-26 01:02:09 +02:00
EMVCJSONRPCInvalidRequest = class( EMVCJSONRPCErrorResponse)
2017-09-24 19:40:40 +02:00
public
constructor Create;
end ;
2017-09-26 01:02:09 +02:00
EMVCJSONRPCMethodNotFound = class( EMVCJSONRPCErrorResponse)
2017-09-24 19:40:40 +02:00
public
constructor Create;
end ;
2017-09-26 01:02:09 +02:00
EMVCJSONRPCInvalidParams = class( EMVCJSONRPCErrorResponse)
2017-09-24 19:40:40 +02:00
public
2017-09-28 00:14:34 +02:00
constructor Create( const message : string ) ;
2017-09-24 19:40:40 +02:00
end ;
2017-09-26 01:02:09 +02:00
EMVCJSONRPCInternalError = class( EMVCJSONRPCErrorResponse)
2017-09-24 19:40:40 +02:00
public
constructor Create;
end ;
{ -32000 to -32099 Server error Reserved for implementation-defined server-errors. }
2017-09-26 01:02:09 +02:00
EMVCJSONRPCServerError = class( EMVCJSONRPCErrorResponse)
2017-09-24 19:40:40 +02:00
public
2017-09-28 00:14:34 +02:00
constructor Create( const JSONRPCError: Integer ; const message : string ) ;
2017-09-24 19:40:40 +02:00
end ;
2017-09-26 01:02:09 +02:00
TMVCJSONObject = TJsonObject;
2017-09-24 19:40:40 +02:00
TMVCJSONArray = TJDOJsonArray;
TMVCJSONRPCController = class( TMVCController)
2017-10-10 12:19:46 +02:00
private
fSerializer: TMVCJsonDataObjectsSerializer;
function GetSerializer: TMVCJsonDataObjectsSerializer;
2017-09-24 19:40:40 +02:00
protected
2017-09-26 01:02:09 +02:00
function CreateError( const RequestID: TValue; const ErrorCode: Integer ;
2017-09-28 00:14:34 +02:00
const message : string ) : TJsonObject;
function CreateResponse( const RequestID: TValue; const Value: TValue) : TJSONRPCResponse;
2017-09-26 01:02:09 +02:00
function CreateRequest( const JSON: TJsonObject) : TJSONRPCRequest;
2017-10-10 12:19:46 +02:00
function JSONObjectAs< T: class , constructor > ( const JSON: TJsonObject) : T;
2017-09-24 19:40:40 +02:00
public
[ MVCPath]
[ MVCHTTPMethods( [ httpPOST] ) ]
[ MVCConsumes( TMVCMediaType. APPLICATION_JSON) ]
[ MVCProduces( TMVCMediaType. APPLICATION_JSON) ]
2017-09-28 00:14:34 +02:00
procedure index ; virtual ;
2017-10-10 12:19:46 +02:00
destructor Destroy; override ;
2017-09-24 19:40:40 +02:00
end ;
implementation
2017-09-26 01:02:09 +02:00
uses
2017-10-10 12:19:46 +02:00
MVCFramework. Serializer. Intf, System. TypInfo;
2017-09-26 01:02:09 +02:00
function JSONDataValueToTValue( const JSONDataValue: TJsonDataValueHelper) : TValue;
begin
case JSONDataValue. Typ of
jdtString:
begin
Result : = JSONDataValue. Value;
end ;
jdtFloat:
begin
Result : = JSONDataValue. FloatValue;
end ;
jdtBool:
begin
Result : = JSONDataValue. BoolValue;
end ;
jdtArray:
begin
2017-09-28 00:14:34 +02:00
Result : = TJsonArray. Parse( JSONDataValue. ArrayValue. ToJSON) as TJsonArray;
2017-09-26 01:02:09 +02:00
end ;
jdtObject:
begin
2017-09-28 00:14:34 +02:00
Result : = TJsonobject. Parse( JSONDataValue. ObjectValue. ToJSON) as TJsonObject;
2017-09-26 01:02:09 +02:00
end ;
jdtInt:
begin
Result : = JSONDataValue. IntValue;
end ;
jdtLong:
begin
Result : = JSONDataValue. LongValue;
end ;
jdtULong:
begin
Result : = JSONDataValue. ULongValue;
end ;
else
raise EMVCJSONRPCException. Create( 'Invalid parameter type' ) ;
end ;
end ;
procedure TValueToJsonElement( const Value: TValue; const JSON: TJSONObject; const KeyName: string ) ;
var
lSer: TMVCJsonDataObjectsSerializer;
begin
case Value. Kind of
tkInteger:
begin
JSON. I[ JSONRPC_RESULT] : = Value. AsInteger;
end ;
tkFloat:
begin
JSON. D[ JSONRPC_RESULT] : = Value. AsExtended;
end ;
tkString, tkUString, tkWChar, tkLString, tkWString:
begin
JSON. S[ JSONRPC_RESULT] : = Value. AsString;
end ;
tkInt64:
begin
JSON. I[ JSONRPC_RESULT] : = Value. AsInt64;
end ;
tkClass:
begin
if Value. AsObject is TJsonObject then
begin
JSON. O[ JSONRPC_RESULT] : = TJsonObject. Create;
JSON. O[ JSONRPC_RESULT] . Assign( TJsonObject( Value. AsObject) ) ;
end
else if Value. AsObject is TJsonArray then
begin
JSON. A[ JSONRPC_RESULT] : = TJsonArray. Create;
JSON. A[ JSONRPC_RESULT] . Assign( TJsonArray( Value. AsObject) ) ;
end
else if Value. AsObject is TDataSet then
begin
lSer : = TMVCJsonDataObjectsSerializer. Create;
try
JSON. A[ JSONRPC_RESULT] : = TJsonArray. Create;
lSer. DataSetToJsonArray( TDataSet( Value. AsObject) , JSON. A[ JSONRPC_RESULT] , TMVCNameCase. ncLowerCase, [ ] ) ;
finally
lSer. Free;
end ;
end
else
begin
lSer : = TMVCJsonDataObjectsSerializer. Create;
try
JSON. O[ JSONRPC_RESULT] : = TJsonObject. Create;
lSer. ObjectToJsonObject( Value. AsObject, JSON. O[ JSONRPC_RESULT] , TMVCSerializationType. stProperties, [ ] ) ;
finally
lSer. Free;
end ;
end ;
end ;
else
raise EMVCJSONRPCException. Create( 'Invalid parameter type' ) ;
end ;
end ;
2017-09-28 00:14:34 +02:00
procedure AppendTValueToJsonArray( const Value: TValue; const JSONArr: TJsonArray) ;
var
lSer: TMVCJsonDataObjectsSerializer;
lJArr: TJsonArray;
lJObj: TJsonObject;
begin
case Value. Kind of
tkInteger:
begin
JSONArr. Add( Value. AsInteger) ;
end ;
tkFloat:
begin
JSONArr. Add( Value. AsExtended) ;
end ;
tkString, tkUString, tkWChar, tkLString, tkWString:
begin
JSONArr. Add( Value. AsString) ;
end ;
tkInt64:
begin
JSONArr. Add( Value. AsInt64) ;
end ;
tkClass:
begin
if Value. AsObject is TJsonObject then
begin
lJObj : = TJsonObject. Create;
JSONArr. Add( lJObj) ;
lJObj. Assign( TJsonObject( Value. AsObject) ) ;
end
else if Value. AsObject is TJsonArray then
begin
lJArr : = TJsonArray. Create;
JSONArr. Add( lJArr) ;
lJArr. Assign( TJsonArray( Value. AsObject) ) ;
end
else if Value. AsObject is TDataSet then
begin
lSer : = TMVCJsonDataObjectsSerializer. Create;
try
lJArr : = TJsonArray. Create;
JSONArr. Add( lJArr) ;
lSer. DataSetToJsonArray( TDataSet( Value. AsObject) , lJArr, TMVCNameCase. ncLowerCase, [ ] ) ;
finally
lSer. Free;
end
end
else
begin
lSer : = TMVCJsonDataObjectsSerializer. Create;
try
lJObj : = TJsonObject. Create;
JSONArr. Add( lJObj) ;
lSer. ObjectToJsonObject( Value. AsObject, lJObj, TMVCSerializationType. stProperties, [ ] ) ;
finally
lSer. Free;
end ;
end ;
end ;
else
raise EMVCJSONRPCException. Create( 'Invalid parameter type' ) ;
end ;
end ;
2017-09-26 01:02:09 +02:00
function StringToJSON( const aValue: string ) : TJsonObject;
var
lJSON: TJSONObject;
begin
lJSON : = nil ;
try
lJSON : = TJsonObject. Parse( aValue) as TJSONObject;
Result : = lJSON;
except
on E: Exception do
begin
lJSON. Free;
raise EMVCJSONRPCParseError. Create;
end ;
end ;
end ;
2017-09-24 19:40:40 +02:00
{ TMVCJSONRPCMessage }
2017-09-26 01:02:09 +02:00
function TMVCJSONRPCMessage. AsJSON: TJsonObject;
2017-09-24 19:40:40 +02:00
begin
Result : = TMVCJSONObject. Create;
Result . S[ JSONRPC_HEADER] : = JSONRPC_VERSION;
end ;
2017-09-26 01:02:09 +02:00
function TMVCJSONRPCMessage. AsJSONRPCMessage: string ;
begin
Result : = fJSON. ToJSON( ) ;
end ;
2017-09-28 00:14:34 +02:00
class
procedure TMVCJSONRPCMessage. CheckID( const aJSON: TMVCJSONObject; out aIsNotification: Boolean ) ;
2017-09-24 19:40:40 +02:00
begin
{
id
An identifier established by the Client that MUST contain a String , Number, or NULL value if included.
If it is not included it is assumed to be a notification.
The value SHOULD normally not be Null [ 1 ] and Numbers SHOULD NOT contain fractional parts [ 2 ]
}
aIsNotification : = not aJSON. Contains( JSONRPC_ID) ;
if not aIsNotification then
begin
if not( aJSON. Types[ JSONRPC_ID] in [ jdtString, jdtInt, jdtLong, jdtULong, jdtNone] ) then
2017-09-26 01:02:09 +02:00
raise EMVCJSONRPCException. Create( 'Message is not a notification but its ' 'id' ' property is not valid' ) ;
2017-09-24 19:40:40 +02:00
end ;
end ;
2017-09-28 00:14:34 +02:00
class
procedure TMVCJSONRPCMessage. CheckMethod( const aJSON: TMVCJSONObject) ;
2017-09-24 19:40:40 +02:00
begin
if ( aJSON. Types[ JSONRPC_METHOD] < > jdtString) then
raise EMVCJSONRPCException. Create( 'Invalid ' 'method' '' ) ;
end ;
2017-09-28 00:14:34 +02:00
class
procedure TMVCJSONRPCMessage. CheckVersion( const aJSON: TMVCJSONObject) ;
2017-09-24 19:40:40 +02:00
begin
if not Assigned( aJSON) then
raise EMVCJSONRPCException. Create( 'JSON not assigned' ) ;
if aJSON. S[ JSONRPC_HEADER] < > JSONRPC_VERSION then
raise EMVCJSONRPCInvalidVersion. Create( JSONRPC_HEADER + ' must be "2.0"' ) ;
end ;
2017-09-26 01:02:09 +02:00
constructor TMVCJSONRPCMessage. Create;
2017-09-24 19:40:40 +02:00
begin
inherited Create;
end ;
2017-09-26 01:02:09 +02:00
{ TMVCJSONRPCController }
2017-09-24 19:40:40 +02:00
2017-09-26 01:02:09 +02:00
function TMVCJSONRPCController. CreateError( const RequestID: TValue; const ErrorCode: Integer ;
2017-09-28 00:14:34 +02:00
const message : string ) : TJsonObject;
2017-09-26 01:02:09 +02:00
var
2017-09-28 00:14:34 +02:00
lErrResp: TJSONRPCResponse;
2017-09-24 19:40:40 +02:00
begin
2017-09-28 00:14:34 +02:00
lErrResp : = TJSONRPCResponse. Create;
2017-09-26 01:02:09 +02:00
try
2017-10-10 12:19:46 +02:00
lErrResp. RequestID : = RequestID;
2017-09-28 00:14:34 +02:00
lErrResp. Error : = TJSONRPCResponse. TJSONRPCResponseError. Create;
2017-09-26 01:02:09 +02:00
lErrResp. Error. Code : = ErrorCode;
2017-09-28 00:14:34 +02:00
lErrResp. Error. ErrMessage : = message ;
2017-09-26 01:02:09 +02:00
Result : = lErrResp. AsJSON;
finally
lErrResp. Free;
end ;
2017-09-24 19:40:40 +02:00
end ;
2017-09-26 01:02:09 +02:00
function TMVCJSONRPCController. CreateRequest(
const JSON: TJsonObject) : TJSONRPCRequest;
var
I: Integer ;
lParams: TJsonArray;
2017-09-24 19:40:40 +02:00
begin
2017-09-28 00:14:34 +02:00
try
Result : = TJSONRPCRequest. Create;
if JSON. Types[ JSONRPC_ID] = jdtString then
2017-10-10 12:19:46 +02:00
Result . RequestID : = JSON. S[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtInt then
2017-10-10 12:19:46 +02:00
Result . RequestID : = JSON. I[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtLong then
2017-10-10 12:19:46 +02:00
Result . RequestID : = JSON. L[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtULong then
2017-10-10 12:19:46 +02:00
Result . RequestID : = JSON. U[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else
2017-10-10 12:19:46 +02:00
Result . RequestID : = TValue. Empty;
2017-09-24 19:40:40 +02:00
2017-09-28 00:14:34 +02:00
Result . Method : = JSON. S[ JSONRPC_METHOD] ;
2017-09-26 01:02:09 +02:00
2017-09-28 00:14:34 +02:00
if JSON. Types[ JSONRPC_PARAMS] = jdtArray then
2017-09-26 01:02:09 +02:00
begin
2017-09-28 00:14:34 +02:00
lParams : = JSON. A[ JSONRPC_PARAMS] ;
for I : = 0 to lParams. Count - 1 do
begin
Result . Params. Add( JSONDataValueToTValue( lParams[ I] ) ) ;
end ;
end
else if JSON. Types[ JSONRPC_PARAMS] < > jdtNone then
begin
raise EMVCJSONRPCException. Create( 'Params must be a JSON array or null' ) ;
2017-09-26 01:02:09 +02:00
end ;
2017-09-28 00:14:34 +02:00
finally
JSON. Free;
2017-09-26 01:02:09 +02:00
end ;
2017-09-24 19:40:40 +02:00
end ;
2017-09-28 00:14:34 +02:00
function TMVCJSONRPCController. CreateResponse( const RequestID: TValue; const Value: TValue) : TJSONRPCResponse;
2017-09-24 19:40:40 +02:00
begin
2017-09-28 00:14:34 +02:00
Result : = TJSONRPCResponse. Create;
2017-10-10 12:19:46 +02:00
Result . RequestID : = RequestID;
2017-09-26 01:02:09 +02:00
Result . Result : = Value;
2017-09-24 19:40:40 +02:00
end ;
2017-10-10 12:19:46 +02:00
destructor TMVCJSONRPCController. Destroy;
begin
fSerializer. Free;
inherited ;
end ;
function TMVCJSONRPCController. GetSerializer: TMVCJsonDataObjectsSerializer;
begin
if not Assigned( fSerializer) then
fSerializer : = TMVCJsonDataObjectsSerializer. Create;
Result : = fSerializer;
end ;
2017-09-24 19:40:40 +02:00
procedure TMVCJSONRPCController. Index ;
var
2017-09-26 01:02:09 +02:00
lJSONRPCReq: TJSONRPCRequest;
2017-09-24 19:40:40 +02:00
lMethod: string ;
lRTTI: TRTTIContext;
lRTTIType: TRttiType;
lRTTIMethod: TRttiMethod;
lRTTIMethodParams: TArray< TRttiParameter> ;
lRes: TValue;
2017-09-28 00:14:34 +02:00
lJSONRPCResponse: TJSONRPCResponse;
2017-09-24 19:40:40 +02:00
lParamsToInject: TArray< TValue> ;
2017-09-26 01:02:09 +02:00
lReqID: TValue;
2017-10-10 12:19:46 +02:00
lRTTIMethodParam: TRttiParameter;
2017-09-24 19:40:40 +02:00
begin
2017-09-26 01:02:09 +02:00
lReqID : = TValue. Empty;
2017-09-24 19:40:40 +02:00
SetLength( lParamsToInject, 0 ) ;
try
2017-09-26 01:02:09 +02:00
lJSONRPCReq : = CreateRequest( StringToJSON( Context. Request. Body) ) ;
2017-09-24 19:40:40 +02:00
try
2017-09-26 01:02:09 +02:00
lMethod : = lJSONRPCReq. Method;
lRTTI : = TRTTIContext. Create;
try
lRTTIType : = lRTTI. GetType( ClassType) ;
lRTTIMethod : = lRTTIType. GetMethod( lMethod) ;
if Assigned( lRTTIMethod) then
2017-09-24 19:40:40 +02:00
begin
2017-09-26 01:02:09 +02:00
lRTTIMethodParams : = lRTTIMethod. GetParameters;
if ( Length( lRTTIMethodParams) < > lJSONRPCReq. Params. Count) then
raise EMVCJSONRPCInvalidParams. Create( 'Wrong parameters count' ) ;
2017-10-10 12:19:46 +02:00
for lRTTIMethodParam in lRTTIMethodParams do
begin
if lRTTIMethodParam. Flags * [ pfVar, pfOut, pfArray, pfReference] < > [ ] then
raise EMVCJSONRPCInvalidParams. CreateFmt( 'Parameter modifier not supported for formal parameter [%s]. Only const and value modifiers are allowed.' , [ lRTTIMethodParam. Name ] )
end ;
2017-09-26 01:02:09 +02:00
try
2017-09-24 19:40:40 +02:00
try
2017-09-26 01:02:09 +02:00
lRes : = lRTTIMethod. Invoke( Self, lJSONRPCReq. Params. ToArray) ;
except
on E: EInvalidCast do
begin
raise EMVCJSONRPCInvalidParams. Create( 'Check your input parameters types' ) ;
end ;
end ;
if lJSONRPCReq. RequestType = TJSONRPCRequestType. Notification then
begin
2017-09-28 00:14:34 +02:00
if lRes. IsObjectInstance then
lRes. AsObject. Free;
2017-09-26 01:02:09 +02:00
ResponseStatus( HTTP_STATUS. NoContent) ;
end
else
begin
2017-10-10 12:19:46 +02:00
lJSONRPCResponse : = CreateResponse( lJSONRPCReq. RequestID, lRes) ;
2017-09-26 01:02:09 +02:00
try
ResponseStatus( 2 0 0 ) ;
Render( lJSONRPCResponse. AsJSON) ;
finally
lJSONRPCResponse. Free;
end ;
2017-09-24 19:40:40 +02:00
end ;
2017-09-26 01:02:09 +02:00
finally
2017-09-28 00:14:34 +02:00
// if lRes.IsObject or lres.IsObjectInstance then
// lRes.AsObject.Free;
2017-09-24 19:40:40 +02:00
end ;
2017-09-26 01:02:09 +02:00
end
else
raise EMVCJSONRPCMethodNotFound. Create;
finally
lRTTI. Free;
end ;
2017-09-24 19:40:40 +02:00
finally
2017-09-26 01:02:09 +02:00
lJSONRPCReq. Free;
2017-09-24 19:40:40 +02:00
end ;
2017-09-26 01:02:09 +02:00
except
on E: EMVCJSONRPCErrorResponse do
begin
{
http: //www.jsonrpc.org/historical/json-rpc-over-http.html#response-codes
HTTP Status code message
5 0 0 - 3 2 7 0 0 Parse error.
4 0 0 - 3 2 6 0 0 Invalid Request.
4 0 4 - 3 2 6 0 1 Method not found.
5 0 0 - 3 2 6 0 2 Invalid params.
5 0 0 - 3 2 6 0 3 Internal error.
5 0 0 - 3 2 0 9 9 .. - 3 2 0 0 0 Server error.
}
case E. JSONRPCErrorCode of
- 3 2 7 0 0 : ResponseStatus( 5 0 0 ) ;
- 3 2 6 0 0 : ResponseStatus( 4 0 0 ) ;
- 3 2 6 0 1 : ResponseStatus( 4 0 4 ) ;
- 3 2 6 0 2 : ResponseStatus( 5 0 0 ) ;
- 3 2 6 0 3 : ResponseStatus( 5 0 0 ) ;
- 3 2 0 9 9 .. - 3 2 0 0 0 : ResponseStatus( 5 0 0 ) ;
2017-09-24 19:40:40 +02:00
end ;
2017-09-26 01:02:09 +02:00
Render( CreateError( lReqID, E. JSONRPCErrorCode, E. Message ) , True ) ;
end ;
on E: EMVCJSONRPCException do
begin
Render( CreateError( lReqID, 0 , E. Message ) , True ) ;
end ;
2017-09-24 19:40:40 +02:00
end ;
end ;
2017-10-10 12:19:46 +02:00
function TMVCJSONRPCController. JSONObjectAs< T> ( const JSON: TJsonObject) : T;
begin
Result : = T. Create;
try
GetSerializer. JsonObjectToObject( JSON, Result , TMVCSerializationType. stProperties, [ ] ) ;
except
Result . Free;
raise ;
end ;
end ;
2017-09-24 19:40:40 +02:00
{ EMVCJSONRPCParseError }
constructor EMVCJSONRPCParseError. Create;
begin
2017-09-26 01:02:09 +02:00
inherited Create( 'Parse error. Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text' ) ;
2017-09-24 19:40:40 +02:00
FJSONRPCErrorCode : = - 3 2 7 0 0 ;
end ;
{ EMVCJSONRPCInvalidRequest }
constructor EMVCJSONRPCInvalidRequest. Create;
begin
2017-09-26 01:02:09 +02:00
inherited Create( 'Invalid Request. The JSON sent is not a valid Request object.' ) ;
2017-09-24 19:40:40 +02:00
FJSONRPCErrorCode : = - 3 2 6 0 0 ;
end ;
{ EMVCJSONRPCMethodNotFound }
constructor EMVCJSONRPCMethodNotFound. Create;
begin
2017-09-26 01:02:09 +02:00
inherited Create( 'Method not found. The method does not exist / is not available' ) ;
2017-09-24 19:40:40 +02:00
FJSONRPCErrorCode : = - 3 2 6 0 1 ;
end ;
{ EMVCJSONRPCInvalidParams }
2017-09-26 01:02:09 +02:00
constructor EMVCJSONRPCInvalidParams. Create( const message : string ) ;
2017-09-24 19:40:40 +02:00
begin
inherited Create( 'Invalid params. [hint: ' + message + ']' ) ;
FJSONRPCErrorCode : = - 3 2 6 0 2 ;
end ;
{ EMVCJSONRPCInternalError }
constructor EMVCJSONRPCInternalError. Create;
begin
inherited Create( 'Internal JSON-RPC error' ) ;
FJSONRPCErrorCode : = - 3 2 6 0 3 ;
end ;
{ EMVCJSONRPCServerError }
2017-09-26 01:02:09 +02:00
constructor EMVCJSONRPCServerError. Create( const JSONRPCError: Integer ; const message : string ) ;
2017-09-24 19:40:40 +02:00
begin
inherited Create( message ) ;
FJSONRPCErrorCode : = JSONRPCError;
end ;
2017-09-26 01:02:09 +02:00
{ TJSONRPCRequest }
constructor TJSONRPCRequest. Create;
begin
inherited Create;
FParams : = TJSONRPCRequestParams. Create;
end ;
destructor TJSONRPCRequest. Destroy;
var
lValue: TValue;
begin
for lValue in FParams do
begin
if lValue. IsObjectInstance then
lValue. AsObject. Free;
end ;
FParams. Free;
inherited ;
end ;
function TJSONRPCRequest. GetJSON: TJsonObject;
var
I: Integer ;
begin
2017-09-28 00:14:34 +02:00
if FMethod. IsEmpty then
raise EMVCJSONRPCException. Create( 'JSON-RPC "Method" cannot be empty' ) ;
2017-09-26 01:02:09 +02:00
Result : = inherited ;
Result . S[ JSONRPC_METHOD] : = FMethod;
2017-09-28 00:14:34 +02:00
if FParams. Count > 0 then
2017-09-26 01:02:09 +02:00
begin
2017-09-28 00:14:34 +02:00
for I : = 0 to FParams. Count - 1 do
begin
AppendTValueToJsonArray( FParams[ I] , Result . A[ JSONRPC_PARAMS] ) ;
end ;
2017-09-26 01:02:09 +02:00
end ;
end ;
function TJSONRPCRequest. GetRequestType: TJSONRPCRequestType;
begin
if FID. IsEmpty then
Result : = TJSONRPCRequestType. Notification
else
Result : = TJSONRPCRequestType. Request;
end ;
2017-09-28 00:14:34 +02:00
procedure TJSONRPCRequest. SetJSON( const JSON: TJsonObject) ;
var
I: Integer ;
lParams: TJsonArray;
begin
if JSON. Types[ JSONRPC_ID] = jdtString then
2017-10-10 12:19:46 +02:00
RequestID : = JSON. S[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtInt then
2017-10-10 12:19:46 +02:00
RequestID : = JSON. I[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtLong then
2017-10-10 12:19:46 +02:00
RequestID : = JSON. L[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtULong then
2017-10-10 12:19:46 +02:00
RequestID : = JSON. U[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else
2017-10-10 12:19:46 +02:00
RequestID : = TValue. Empty;
2017-09-28 00:14:34 +02:00
Method : = JSON. S[ JSONRPC_METHOD] ;
Params. Clear;
if JSON. Types[ JSONRPC_PARAMS] = jdtArray then
begin
lParams : = JSON. A[ JSONRPC_PARAMS] ;
for I : = 0 to lParams. Count - 1 do
begin
Params. Add( JSONDataValueToTValue( lParams[ I] ) ) ;
end ;
end
else if JSON. Types[ JSONRPC_PARAMS] < > jdtNone then
begin
raise EMVCJSONRPCException. Create( 'Params must be a JSON array or null' ) ;
end ;
end ;
2017-09-26 01:02:09 +02:00
procedure TJSONRPCRequest. SetMethod( const Value: string ) ;
begin
FMethod : = Value;
end ;
{ TJSONRCPResponse }
2017-09-28 00:14:34 +02:00
constructor TJSONRPCResponse. Create;
2017-09-26 01:02:09 +02:00
begin
inherited ;
FError : = nil ;
end ;
2017-09-28 00:14:34 +02:00
destructor TJSONRPCResponse. Destroy;
2017-09-26 01:02:09 +02:00
begin
FreeAndNil( FError) ;
2017-09-28 00:14:34 +02:00
if FResult. IsObjectInstance then
FResult. AsObject. Free;
2017-09-26 01:02:09 +02:00
inherited ;
end ;
2017-09-28 00:14:34 +02:00
function TJSONRPCResponse. GetJSON: TJsonObject;
2017-09-26 01:02:09 +02:00
begin
Result : = inherited ;
// Must generate something like the following:
// {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}
try
if Assigned( FError) then
begin
Result . O[ JSONRPC_ERROR] . I[ JSONRPC_CODE] : = FError. Code;
2017-09-28 00:14:34 +02:00
Result . O[ JSONRPC_ERROR] . S[ JSONRPC_MESSAGE] : = FError. ErrMessage;
2017-09-26 01:02:09 +02:00
end
else
begin
TValueToJsonElement( Self. FResult, Result , JSONRPC_RESULT) ;
end ;
except
Result . Free;
raise ;
end ;
end ;
2017-09-28 00:14:34 +02:00
procedure TJSONRPCResponse. SetError( const Value: TJSONRPCResponseError) ;
2017-09-26 01:02:09 +02:00
begin
FError : = Value;
end ;
2017-09-28 00:14:34 +02:00
procedure TJSONRPCResponse. SetJSON( const JSON: TJsonObject) ;
begin
if JSON. Types[ JSONRPC_ID] = jdtString then
2017-10-10 12:19:46 +02:00
RequestID : = JSON. S[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtInt then
2017-10-10 12:19:46 +02:00
RequestID : = JSON. I[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtLong then
2017-10-10 12:19:46 +02:00
RequestID : = JSON. L[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else if JSON. Types[ JSONRPC_ID] = jdtULong then
2017-10-10 12:19:46 +02:00
RequestID : = JSON. U[ JSONRPC_ID]
2017-09-28 00:14:34 +02:00
else
2017-10-10 12:19:46 +02:00
RequestID : = TValue. Empty;
2017-09-28 00:14:34 +02:00
if JSON. Contains( JSONRPC_RESULT) then
begin
FreeAndNil( FError) ;
FResult : = JSONDataValueToTValue( JSON. Values[ JSONRPC_RESULT] ) ;
end
else
begin
FResult : = TValue. Empty;
if JSON. Contains( JSONRPC_ERROR) then
begin
FError : = TJSONRPCResponseError. Create;
FError. Code : = JSON. O[ JSONRPC_ERROR] . I[ JSONRPC_CODE] ;
FError. ErrMessage : = JSON. O[ JSONRPC_ERROR] . S[ JSONRPC_MESSAGE] ;
end
else
raise EMVCJSONRPCException. Create( 'Response message must have ' 'result' ' or ' 'error' '' ) ;
end ;
end ;
procedure TJSONRPCResponse. SetResult( const Value: TValue) ;
2017-09-26 01:02:09 +02:00
begin
FResult : = Value;
end ;
{ TJSONRPCMessage }
constructor TJSONRPCMessage. Create;
begin
inherited ;
end ;
function TJSONRPCMessage. GetJSON: TJsonObject;
begin
Result : = TJsonObject. Create;
Result . S[ JSONRPC_HEADER] : = JSONRPC_VERSION;
if not FID. IsEmpty then
begin
if FID. IsType< string > then
begin
Result . S[ JSONRPC_ID] : = FID. AsString;
end
else if FID. IsType< Int32 > then
begin
Result . I[ JSONRPC_ID] : = FID. AsInteger;
end
else if FID. IsType< Int64 > then
begin
Result . I[ JSONRPC_ID] : = FID. AsInt64;
end
else
raise EMVCJSONRPCException. Create( 'ID can be only Int32, Int64 or String' ) ;
end ;
end ;
function TJSONRPCMessage. GetJSONString: string ;
var
lJSON: TJsonObject;
begin
lJSON : = GetJSON;
try
Result : = lJSON. ToJson;
finally
lJSON. Free;
end ;
end ;
procedure TJSONRPCMessage. SetID( const Value: TValue) ;
begin
FID : = Value;
end ;
2017-09-28 00:14:34 +02:00
procedure TJSONRPCMessage. SetJsonString( const Value: string ) ;
var
lJSON: TJsonObject;
begin
try
lJSON : = TJsonObject. Parse( Value) as TJsonObject;
except
raise EMVCJSONRPCParseError. Create;
end ;
try
AsJSON : = lJSON;
finally
lJSON. Free;
end ;
end ;
2017-09-26 01:02:09 +02:00
{ TJSONRPCResponseError }
2017-09-28 00:14:34 +02:00
procedure TJSONRPCResponse. TJSONRPCResponseError. SetCode( const Value: Integer ) ;
2017-09-26 01:02:09 +02:00
begin
FCode : = Value;
end ;
2017-09-28 00:14:34 +02:00
procedure TJSONRPCResponse. TJSONRPCResponseError. SetMessage( const Value: string ) ;
2017-09-26 01:02:09 +02:00
begin
FMessage : = Value;
end ;
2017-09-24 19:40:40 +02:00
end .