Added '/describe' to json-rpc

Added 'MVCInheritable' attribute
This commit is contained in:
Daniele Teti 2019-04-03 09:42:15 +02:00
parent 3a0981f207
commit d20ef3fe41
10 changed files with 259 additions and 310 deletions

View File

@ -112,6 +112,8 @@ Render(lPerson, False,
- Fixed! [issue182](https://github.com/danieleteti/delphimvcframework/issues/182)
- New! `Dict(array of string, array of string)` function allows to render a dictionary of strings in a really simple way.
- Improved! Exceptions rendering while using MIME types different to `application/json`.
- Improved! JSONRPC Automatic Object Publishing can not invoke inherited methods if not explicitely defined with `MVCInheritable` attribute.
- New! Calling `<jsonrpcendpoint>/describe` returns the methods list available for that endpoint.
- New Installation procedure! Just open the project group, build all and install the design-time package (which is `dmvcframeworkDT`)

View File

@ -149,7 +149,7 @@ begin
fLogsFolder := TPath.GetDirectoryName(GetModuleName(HInstance));
{$ENDIF}
{$IF Defined(Android) or Defined(IOS)}
fLogsFolder := TPath.GetSharedDocumentsPath();
fLogsFolder := TPath.GetPublicPath();
{$ENDIF}
end;
if not TDirectory.Exists(fLogsFolder) then

View File

@ -55,23 +55,15 @@
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="Android\Debug\AndroidManifest.xml" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\</RemoteDir>
<RemoteName>AndroidManifest.xml</RemoteName>
<DeployClass>ProjectAndroidManifest</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\lib\android\debug\armeabi\libnative-activity.so" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\library\lib\armeabi\</RemoteDir>
<DeployFile Include="Android\Debug\libMobileRESTAppenderSample.so" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\library\lib\armeabi-v7a\</RemoteDir>
<RemoteName>libMobileRESTAppenderSample.so</RemoteName>
<DeployClass>AndroidLibnativeArmeabiFile</DeployClass>
<DeployClass>ProjectOutput</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
<Required>True</Required>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-hdpi\</RemoteDir>
@ -91,20 +83,10 @@
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="Android\Debug\libMobileRESTAppenderSample.so" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\library\lib\armeabi-v7a\</RemoteDir>
<RemoteName>libMobileRESTAppenderSample.so</RemoteName>
<DeployClass>ProjectOutput</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
<Required>True</Required>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-xxhdpi\</RemoteDir>
<RemoteName>ic_launcher.png</RemoteName>
<DeployClass>Android_LauncherIcon144</DeployClass>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-xlarge\</RemoteDir>
<RemoteName>splash_image.png</RemoteName>
<DeployClass>Android_SplashImage960</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
@ -119,33 +101,6 @@
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-normal\</RemoteDir>
<RemoteName>splash_image.png</RemoteName>
<DeployClass>Android_SplashImage470</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-xhdpi\</RemoteDir>
<RemoteName>ic_launcher.png</RemoteName>
<DeployClass>Android_LauncherIcon96</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-xlarge\</RemoteDir>
<RemoteName>splash_image.png</RemoteName>
<DeployClass>Android_SplashImage960</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-mdpi\</RemoteDir>
<RemoteName>ic_launcher.png</RemoteName>
@ -200,17 +155,62 @@
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
</ItemGroup>
<ItemGroup Condition="'$(Platform)'=='iOSSimulator'">
<DeployFile Include="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib">
<RemoteDir>MobileRESTAppenderSample.app\</RemoteDir>
<RemoteName>libcgunwind.1.0.dylib</RemoteName>
<DeployClass>DependencyModule</DeployClass>
<DeployFile Include="Android\Debug\AndroidManifest.xml" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\</RemoteDir>
<RemoteName>AndroidManifest.xml</RemoteName>
<DeployClass>ProjectAndroidManifest</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\lib\android\debug\armeabi\libnative-activity.so" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\library\lib\armeabi\</RemoteDir>
<RemoteName>libMobileRESTAppenderSample.so</RemoteName>
<DeployClass>AndroidLibnativeArmeabiFile</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-xxhdpi\</RemoteDir>
<RemoteName>ic_launcher.png</RemoteName>
<DeployClass>Android_LauncherIcon144</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-normal\</RemoteDir>
<RemoteName>splash_image.png</RemoteName>
<DeployClass>Android_SplashImage470</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\drawable-xhdpi\</RemoteDir>
<RemoteName>ic_launcher.png</RemoteName>
<DeployClass>Android_LauncherIcon96</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="Android\Debug\styles-v21.xml" Condition="'$(Config)'=='Debug'">
<RemoteDir>MobileRESTAppenderSample\res\values-v21\</RemoteDir>
<RemoteName>styles.xml</RemoteName>
<DeployClass>AndroidSplashStylesV21</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
</ItemGroup>
<ItemGroup Condition="'$(Platform)'=='iOSSimulator'">
<DeployFile Include="$(BDS)\Redist\iossimulator\libPCRE.dylib">
<RemoteDir>MobileRESTAppenderSample.app\</RemoteDir>
<RemoteName>libPCRE.dylib</RemoteName>
@ -220,5 +220,23 @@
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib">
<RemoteDir>MobileRESTAppenderSample.app\</RemoteDir>
<RemoteName>libcgunwind.1.0.dylib</RemoteName>
<DeployClass>DependencyModule</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
<DeployFile Include="$(BDS)\Redist\iossimulator\libpcre.dylib">
<RemoteDir>MobileRESTAppenderSample.app\</RemoteDir>
<RemoteName>libpcre.dylib</RemoteName>
<DeployClass>DependencyModule</DeployClass>
<Operation>1</Operation>
<LocalCommand/>
<RemoteCommand/>
<Overwrite>True</Overwrite>
</DeployFile>
</ItemGroup>
</Project>

View File

@ -43,10 +43,10 @@ requires
loggerproRT;
contains
Web.ApacheConst,
Web.Win.IsapiHTTP,
Web.ApacheHTTP,
Web.HTTPDMethods,
Web.ApacheConst in 'c:\program files (x86)\embarcadero\studio\20.0\source\Internet\Web.ApacheConst.pas',
Web.Win.IsapiHTTP in 'c:\program files (x86)\embarcadero\studio\20.0\source\Internet\Web.Win.IsapiHTTP.pas',
Web.ApacheHTTP in 'c:\program files (x86)\embarcadero\studio\20.0\source\Internet\Web.ApacheHTTP.pas',
Web.HTTPDMethods in 'c:\program files (x86)\embarcadero\studio\20.0\source\Internet\Web.HTTPDMethods.pas',
Web.HTTPDImpl,
JsonDataObjects in '..\..\sources\JsonDataObjects.pas',
MVCFramework.ActiveRecord in '..\..\sources\MVCFramework.ActiveRecord.pas',

View File

@ -74,12 +74,14 @@
<DCC_RemoteDebug>true</DCC_RemoteDebug>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<Debugger_HostApplication>C:\DEV\dmscontainer_centrosoftware\bin\DMSContainerService.exe</Debugger_HostApplication>
<Debugger_HostApplication>C:\DEV\dmscontainer2\bin\DMSContainerService.exe</Debugger_HostApplication>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
<DCC_MapFile>3</DCC_MapFile>
<DCC_Description>DMVCFramework 3.x</DCC_Description>
<DllSuffix>103</DllSuffix>
<Debugger_IncludeSystemVars>true</Debugger_IncludeSystemVars>
<Debugger_EnvVars>DMS_DEBUG=true;DMS_HOME=C:\DEV\dmscontainer2\bin\;$(Debugger_EnvVars)</Debugger_EnvVars>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
@ -172,11 +174,11 @@
<Source Name="MainSource">dmvcframeworkRT.dpk</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="C:\Users\Public\Documents\Embarcadero\Studio\20.0\Bpl\LockBoxFMXDD250.bpl">TurboPack LockBox Delphi FMX designtime package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\DataExplorerDBXPluginEnt260.bpl">DBExpress Enterprise Data Explorer Integration</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k260.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp260.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k260.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp260.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="C:\Program Files (x86)\FastReports\LibD26\dclfrxtee26.bpl">FastReport 6.0 Tee Components</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="3">

View File

@ -97,7 +97,8 @@ type
constructor Create; virtual;
end;
TJSONRPCParamDataType = (pdtString, pdtInteger, pdtLongInteger, pdTJDOJsonObject, pdtJSONArray, pdtBoolean, pdtDate,
TJSONRPCParamDataType = (pdtString, pdtInteger, pdtLongInteger, pdTJDOJsonObject, pdtJSONArray,
pdtBoolean, pdtDate,
pdtTime, pdtDateTime, pdtFloat, pdtObject);
TJSONRPCRequestParams = class
@ -292,12 +293,15 @@ type
fRPCInstance: TObject;
fOwsRPCInstance: Boolean;
function GetSerializer: TMVCJsonDataObjectsSerializer;
// procedure CheckInputParametersTypes(aRTTIMethod: TRTTIMethod);
function GetDeclaredMethod(lMethod: string; lRTTIType: TRttiType): TRTTIMethod;
function GetInheritedMethod(lMethod: string; lRTTIType: TRttiType): TRTTIMethod;
protected
function CreateError(const RequestID: TValue; const ErrorCode: Integer; const Message: string): TJDOJsonObject;
function CreateError(const RequestID: TValue; const ErrorCode: Integer; const Message: string)
: TJDOJsonObject;
function CreateResponse(const RequestID: TValue; const Value: TValue): TJSONRPCResponse;
function CreateRequest(const JSON: TJDOJsonObject): IJSONRPCRequest;
function JSONObjectAs<T: class, constructor>(const JSON: TJDOJsonObject): T;
function CanBeRemotelyInvoked(const RTTIMethod: TRTTIMethod): Boolean;
public
[MVCPath]
[MVCHTTPMethods([httpPOST])]
@ -305,6 +309,10 @@ type
[MVCProduces(TMVCMediaType.APPLICATION_JSON)]
procedure Index; virtual;
[MVCPath('/describe')]
[MVCHTTPMethods([httpGET])]
procedure GetPublishedMethodList; virtual;
[MVCPath]
[MVCHTTPMethods([httpGET])]
[MVCProduces(TMVCMediaType.TEXT_PLAIN)]
@ -315,7 +323,8 @@ type
TMVCJSONRPCPublisher = class(TMVCJSONRPCController)
public
constructor Create(const RPCInstance: TObject; const Owns: Boolean = True); reintroduce; overload;
constructor Create(const RPCInstance: TObject; const Owns: Boolean = True);
reintroduce; overload;
end;
TJSONRPCProxyGenerator = class abstract
@ -329,13 +338,16 @@ type
TJSONRPCProxyGeneratorClass = class of TJSONRPCProxyGenerator;
procedure RegisterJSONRPCProxyGenerator(const aLanguage: String; const aClass: TJSONRPCProxyGeneratorClass);
procedure RegisterJSONRPCProxyGenerator(const aLanguage: String;
const aClass: TJSONRPCProxyGeneratorClass);
implementation
uses
MVCFramework.Serializer.Intf, MVCFramework.Logger,
System.TypInfo, MVCFramework.DuckTyping,
MVCFramework.Serializer.Intf,
MVCFramework.Logger,
System.TypInfo,
MVCFramework.DuckTyping,
MVCFramework.Serializer.jsondataobjects.CustomTypes;
const
@ -414,7 +426,8 @@ begin
begin
lSer := TMVCJsonDataObjectsSerializer.Create;
try
LJObj := lSer.SerializeObjectToJSON(Value.AsObject, TMVCSerializationType.stProperties, [], nil);
LJObj := lSer.SerializeObjectToJSON(Value.AsObject,
TMVCSerializationType.stProperties, [], nil);
JSONArr.Add(LJObj);
finally
lSer.Free;
@ -424,99 +437,6 @@ begin
else
raise EMVCException.Create('Invalid type');
end;
//
// case Value.Kind of
// tkInteger:
// begin
// JSONArr.Add(Value.AsInteger);
// end;
// tkFloat:
// begin
// if Value.IsType(TypeInfo(System.TDateTime), False) then
// begin
// lIsDate := Frac(Value.AsExtended) = 0;
// lIsTime := Trunc(Value.AsExtended) = 0;
// if lIsDate then
// begin
// JSONArr.Add(DateToISODate(Value.AsExtended));
// end
// else if lIsTime then
// begin
// JSONArr.Add(TimeToISOTime(Value.AsExtended));
// end
// else
// begin
// JSONArr.Add(DateTimeToISOTimeStamp(Value.AsExtended));
// end;
// end
// else
// begin
// JSONArr.Add(Value.AsExtended);
// end;
// end;
// tkString, tkUString, tkWChar, tkLString, tkWString:
// begin
// JSONArr.Add(Value.AsString);
// end;
// tkInt64:
// begin
// JSONArr.Add(Value.AsInt64);
// end;
// tkEnumeration:
// begin
// if not Value.TryAsOrdinal(lOrdinalValue) then
// begin
// raise EMVCException.Create('Invalid ordinal parameter');
// end;
// if Value.IsType(TypeInfo(System.Boolean)) then
// begin
// JSONArr.Add(lOrdinalValue = 1);
// end
// else
// begin
// JSONArr.Add(lOrdinalValue);
// end;
// end;
// tkClass:
// begin
// if Value.AsObject is TJDOJsonObject then
// begin
// LJObj := TJDOJsonObject.Create;
// JSONArr.Add(LJObj);
// LJObj.Assign(TJDOJsonObject(Value.AsObject));
// end
// else if Value.AsObject is TJDOJsonArray then
// begin
// lJArr := TJDOJsonArray.Create;
// JSONArr.Add(lJArr);
// lJArr.Assign(TJDOJsonArray(Value.AsObject));
// end
// else if Value.AsObject is TDataSet then
// begin
// lSer := TMVCJsonDataObjectsSerializer.Create;
// try
// lJArr := TJDOJsonArray.Create;
// JSONArr.Add(lJArr);
// lSer.DataSetToJsonArray(TDataSet(Value.AsObject), lJArr, TMVCNameCase.ncLowerCase, []);
// finally
// lSer.Free;
// end
// end
// else
// begin
// lSer := TMVCJsonDataObjectsSerializer.Create;
// try
// LJObj := lSer.SerializeObjectToJSON(Value.AsObject, TMVCSerializationType.stProperties, [], nil);
// JSONArr.Add(LJObj);
// finally
// lSer.Free;
// end;
// end;
// end;
// else
// raise EMVCException.Create('Invalid type');
// end;
end;
function JSONDataValueToTValue(const JSONDataValue: TJsonDataValueHelper): TValue; overload;
@ -564,7 +484,8 @@ begin
Result := RTTIParameter.Name + ': ' + RTTIParameter.ParamType.Name;
end;
procedure JSONDataValueToTValueParam(const JSONDataValue: TJsonDataValueHelper; const RTTIParameter: TRttiParameter;
procedure JSONDataValueToTValueParam(const JSONDataValue: TJsonDataValueHelper;
const RTTIParameter: TRttiParameter;
const JSONRPCRequestParams: TJSONRPCRequestParams);
begin
case RTTIParameter.ParamType.TypeKind of
@ -572,7 +493,8 @@ begin
begin
if JSONDataValue.Typ <> jdtString then
begin
raise EMVCJSONRPCInvalidParams.Create('Invalid param type for [' + BuildDeclaration(RTTIParameter) + ']');
raise EMVCJSONRPCInvalidParams.Create('Invalid param type for [' +
BuildDeclaration(RTTIParameter) + ']');
end;
JSONRPCRequestParams.Add(JSONDataValue.Value);
end;
@ -615,7 +537,8 @@ begin
end
else if SameText(RTTIParameter.ParamType.Name, TJDOJsonObject.ClassName) then
begin
JSONRPCRequestParams.Add(JSONDataValue.ObjectValue.Clone as TJDOJsonObject, pdTJDOJsonObject);
JSONRPCRequestParams.Add(JSONDataValue.ObjectValue.Clone as TJDOJsonObject,
pdTJDOJsonObject);
end
else
begin
@ -652,100 +575,9 @@ begin
end;
else
raise EMVCJSONRPCInvalidRequest.Create('Invalid parameter type for ' + BuildDeclaration(RTTIParameter));
raise EMVCJSONRPCInvalidRequest.Create('Invalid parameter type for ' +
BuildDeclaration(RTTIParameter));
end;
// case JSONDataValue.Typ of
// jdtString:
// begin
// if RTTIParameter.ParamType.TypeKind in [tkString, tkUString, tkUnicodeString, tkAnsiString] then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.Value);
// end
// else if SameText(RTTIParameter.ParamType.Name, 'TDate') then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.UtcDateTimeValue, pdtDate);
// end
// else if SameText(RTTIParameter.ParamType.Name, 'TDateTime') then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.UtcDateTimeValue, pdtDateTime);
// end
// else if SameText(RTTIParameter.ParamType.Name, 'TTime') then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.UtcDateTimeValue, pdtTime);
// end
// else
// raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter));
// end;
// jdtFloat:
// begin
// if RTTIParameter.ParamType.TypeKind in [tkFloat] then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.FloatValue, pdtFloat);
// end
// else
// raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter));
// end;
// jdtBool:
// begin
// if (RTTIParameter.ParamType.TypeKind in [tkEnumeration]) and
// (SameText(RTTIParameter.ParamType.QualifiedName, 'System.Boolean')) then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.BoolValue, pdtBoolean);
// end
// else
// raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter));
// end;
// jdtArray:
// begin
// if (RTTIParameter.ParamType.TypeKind in [tkClass]) and
// (SameText(RTTIParameter.ParamType.Name, TJDOJsonArray.ClassName)) then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.ArrayValue.Clone, pdtJSONArray);
// end
// else
// raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter));
// end;
// jdtObject:
// begin
// if (RTTIParameter.ParamType.TypeKind in [tkClass]) and
// (SameText(RTTIParameter.ParamType.Name, TJDOJsonObject.ClassName)) then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.ObjectValue.Clone as TJDOJsonObject, pdTJDOJsonObject);
// end
// else
// raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter));
// end;
// jdtInt:
// begin
// if RTTIParameter.ParamType.TypeKind in [tkInteger] then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.IntValue, pdtInteger);
// end
// else
// raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter));
// end;
// jdtLong:
// begin
// if RTTIParameter.ParamType.TypeKind in [tkInt64] then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.LongValue, pdtLongInteger);
// end
// else
// raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter));
// end;
// jdtULong:
// begin
// if RTTIParameter.ParamType.TypeKind in [tkInt64] then
// begin
// JSONRPCRequestParams.Add(JSONDataValue.ULongValue, pdtLongInteger);
// end
// else
// raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter));
// end;
// else
// raise EMVCJSONRPCInvalidRequest.Create('Invalid parameter type for ' + BuildDeclaration(RTTIParameter));
// end;
end;
{ TMVCJSONRPCMessage }
@ -761,7 +593,8 @@ begin
Result := fJSON.ToJSON();
end;
class procedure TMVCJSONRPCMessage.CheckID(const aJSON: TMVCJSONObject; out aIsNotification: Boolean);
class procedure TMVCJSONRPCMessage.CheckID(const aJSON: TMVCJSONObject;
out aIsNotification: Boolean);
begin
{
id
@ -773,7 +606,8 @@ begin
if not aIsNotification then
begin
if not(aJSON.Types[JSONRPC_ID] in [jdtString, jdtInt, jdtLong, jdtULong, jdtNone]) then
raise EMVCJSONRPCException.Create('Message is not a notification but its ''id'' property is not valid');
raise EMVCJSONRPCException.Create
('Message is not a notification but its ''id'' property is not valid');
end;
end;
@ -824,6 +658,13 @@ end;
//
// end;
function TMVCJSONRPCController.CanBeRemotelyInvoked(
const RTTIMethod: TRTTIMethod): Boolean;
begin
Result := (RTTIMethod.Visibility = mvPublic) and
(RTTIMethod.MethodKind in [mkProcedure, mkFunction]);
end;
constructor TMVCJSONRPCController.Create;
begin
inherited Create;
@ -831,7 +672,8 @@ begin
fOwsRPCInstance := False;
end;
function TMVCJSONRPCController.CreateError(const RequestID: TValue; const ErrorCode: Integer; const Message: string)
function TMVCJSONRPCController.CreateError(const RequestID: TValue; const ErrorCode: Integer;
const Message: string)
: TJDOJsonObject;
var
lErrResp: TJSONRPCResponse;
@ -883,7 +725,8 @@ begin
}
end;
function TMVCJSONRPCController.CreateResponse(const RequestID: TValue; const Value: TValue): TJSONRPCResponse;
function TMVCJSONRPCController.CreateResponse(const RequestID: TValue; const Value: TValue)
: TJSONRPCResponse;
begin
Result := TJSONRPCResponse.Create;
Result.RequestID := RequestID;
@ -900,6 +743,40 @@ begin
inherited;
end;
function TMVCJSONRPCController.GetDeclaredMethod(lMethod: string; lRTTIType: TRttiType)
: TRTTIMethod;
var
lRTTIDeclaredMethods: TArray<TRTTIMethod>;
I: Integer;
begin
Result := nil;
lRTTIDeclaredMethods := lRTTIType.GetDeclaredMethods;
for I := 0 to Length(lRTTIType.GetDeclaredMethods) - 1 do
begin
if SameText(lRTTIDeclaredMethods[I].Name, lMethod) then
begin
Result := lRTTIDeclaredMethods[I];
Break;
end;
end;
end;
function TMVCJSONRPCController.GetInheritedMethod(lMethod: string;
lRTTIType: TRttiType): TRTTIMethod;
var
lRTTIMethod: TRTTIMethod;
begin
Result := nil;
lRTTIMethod := lRTTIType.GetMethod(lMethod);
if Assigned(lRTTIMethod) then
begin
if TMVCSerializerHelper.HasAttribute<MVCInheritableAttribute>(lRTTIMethod) then
begin
Result := lRTTIMethod;
end;
end;
end;
procedure TMVCJSONRPCController.GetProxyCode;
var
lLanguage: string;
@ -948,6 +825,43 @@ begin
end;
end;
procedure TMVCJSONRPCController.GetPublishedMethodList;
var
lRTTI: TRTTIContext;
lRTTIType: TRttiType;
lRTTIMethodList: TArray<TRTTIMethod>;
lRTTIMethod: TRTTIMethod;
begin
lRTTI := TRTTIContext.Create;
try
lRTTIType := lRTTI.GetType(fRPCInstance.ClassType);
ResponseStream.AppendLine('//Declared methods available on ' + fRPCInstance.QualifiedClassName);
lRTTIMethodList := lRTTIType.GetDeclaredMethods;
for lRTTIMethod in lRTTIMethodList do
begin
if CanBeRemotelyInvoked(lRTTIMethod) then
begin
ResponseStream.AppendLine(lRTTIMethod.ToString);
end;
end;
ResponseStream.AppendLine('//Inherited methods available on ' +
fRPCInstance.QualifiedClassName);
lRTTIMethodList := lRTTIType.BaseType.GetMethods;
for lRTTIMethod in lRTTIMethodList do
begin
if TMVCSerializerHelper.HasAttribute<MVCInheritableAttribute>(lRTTIMethod) and
CanBeRemotelyInvoked(lRTTIMethod) then
begin
ResponseStream.AppendLine(lRTTIMethod.ToString);
end;
end;
RenderResponseStream;
finally
lRTTI.Free;
end;
end;
function TMVCJSONRPCController.GetSerializer: TMVCJsonDataObjectsSerializer;
begin
if not Assigned(fSerializer) then
@ -988,23 +902,32 @@ begin
lRTTI := TRTTIContext.Create;
try
lRTTIType := lRTTI.GetType(fRPCInstance.ClassType);
lRTTIMethod := lRTTIType.GetMethod(lMethod);
lRTTIMethod := GetDeclaredMethod(lMethod, lRTTIType);
if not Assigned(lRTTIMethod) then
begin
lRTTIMethod := GetInheritedMethod(lMethod, lRTTIType);
end;
if Assigned(lRTTIMethod) then
begin
if (lRTTIMethod.Visibility <> mvPublic) or (not(lRTTIMethod.MethodKind in [mkProcedure, mkFunction])) then
if not CanBeRemotelyInvoked(lRTTIMethod) then
begin
LogW(Format('Method "%s" cannot be called. Only public functions or procedures can be called. ',
LogW(Format
('Method "%s" cannot be called. Only public functions or procedures can be called. ',
[lMethod]));
raise EMVCJSONRPCMethodNotFound.Create(lMethod);
end;
if (lJSONRPCReq.RequestType = TJSONRPCRequestType.Request) and (lRTTIMethod.MethodKind <> mkFunction) then
if (lJSONRPCReq.RequestType = TJSONRPCRequestType.Request) and
(lRTTIMethod.MethodKind <> mkFunction) then
begin
raise EMVCJSONRPCInvalidParams.Create
('Cannot call a procedure using a JSON-RPC request. [HINT] Use requests for functions and notifications for procedures');
end;
if (lJSONRPCReq.RequestType = TJSONRPCRequestType.Notification) and (lRTTIMethod.MethodKind <> mkProcedure)
if (lJSONRPCReq.RequestType = TJSONRPCRequestType.Notification) and
(lRTTIMethod.MethodKind <> mkProcedure)
then
begin
raise EMVCJSONRPCInvalidParams.Create
@ -1026,8 +949,6 @@ begin
case lJSONRPCReq.RequestType of
TJSONRPCRequestType.Notification:
begin
// if lRes.IsObject then
// lRes.AsObject.Free;
ResponseStatus(HTTP_STATUS.NoContent);
end;
TJSONRPCRequestType.Request:
@ -1080,7 +1001,8 @@ begin
ResponseStatus(500);
end;
Render(CreateError(lReqID, E.JSONRPCErrorCode, E.Message), True);
LogE(Format('[JSON-RPC][CLS %s][ERR %d][MSG "%s"]', [E.ClassName, E.JSONRPCErrorCode, E.Message]));
LogE(Format('[JSON-RPC][CLS %s][ERR %d][MSG "%s"]', [E.ClassName, E.JSONRPCErrorCode,
E.Message]));
end;
on E: Exception do
begin
@ -1129,7 +1051,8 @@ end;
constructor EMVCJSONRPCMethodNotFound.Create(const MethodName: string);
begin
inherited CreateFmt('Method "%s" not found. The method does not exist or is not available.', [MethodName]);
inherited CreateFmt('Method "%s" not found. The method does not exist or is not available.',
[MethodName]);
FJSONRPCErrorCode := -32601;
end;
@ -1230,7 +1153,8 @@ begin
inherited;
end;
procedure TJSONRPCNotification.FillParameters(const JSON: TJDOJsonObject; const RTTIMethod: TRTTIMethod);
procedure TJSONRPCNotification.FillParameters(const JSON: TJDOJsonObject;
const RTTIMethod: TRTTIMethod);
var
lRTTIMethodParams: TArray<TRttiParameter>;
lRTTIMethodParam: TRttiParameter;
@ -1285,7 +1209,8 @@ begin
begin
for I := 0 to FParams.Count - 1 do
begin
AppendTValueToJsonArray(FParams.FParamsValue[I], FParams.FParamsType[I], Result.A[JSONRPC_PARAMS]);
AppendTValueToJsonArray(FParams.FParamsValue[I], FParams.FParamsType[I],
Result.A[JSONRPC_PARAMS]);
end;
end;
end;
@ -1546,7 +1471,8 @@ begin
inherited;
end;
procedure RegisterJSONRPCProxyGenerator(const aLanguage: String; const aClass: TJSONRPCProxyGeneratorClass);
procedure RegisterJSONRPCProxyGenerator(const aLanguage: String;
const aClass: TJSONRPCProxyGeneratorClass);
begin
if not Assigned(GProxyGeneratorsRegister) then
begin
@ -1659,7 +1585,8 @@ begin
Result := FParamsValue.ToArray;
end;
procedure TJSONRPCRequestParams.Add(const Value: TValue; const ParamType: TJSONRPCParamDataType);
procedure TJSONRPCRequestParams.Add(const Value: TValue;
const ParamType: TJSONRPCParamDataType);
begin
FParamsValue.Add(Value);
FParamsType.Add(ParamType);

View File

@ -123,26 +123,26 @@ end;
procedure TMVCStreamSerializerJsonDataObject.SerializeAttribute(const AElementValue: TValue;
const APropertyName: string; const ASerializerObject: TObject; const AAttributes: TArray<TCustomAttribute>);
var
Stream: TStream;
SS: TStringStream;
lStream: TStream;
lStringStream: TStringStream;
begin
Stream := AElementValue.AsObject as TStream;
if Assigned(Stream) then
lStream := AElementValue.AsObject as TStream;
if Assigned(lStream) then
begin
SS := TStringStream.Create('', TEncoding.Default);
lStringStream := TStringStream.Create('', TEncoding.Default);
try
Stream.Position := 0;
lStream.Position := 0;
if TMVCSerializerHelper.AttributeExists<MVCSerializeAsStringAttribute>(AAttributes) then
begin
SS.CopyFrom(Stream, 0);
lStringStream.CopyFrom(lStream, 0);
end
else
begin
TMVCSerializerHelper.EncodeStream(Stream, SS);
TMVCSerializerHelper.EncodeStream(lStream, lStringStream);
end;
TJsonObject(ASerializerObject).S[APropertyName] := SS.DataString;
TJDOJsonObject(ASerializerObject).S[APropertyName] := lStringStream.DataString;
finally
SS.Free;
lStringStream.Free;
end;
end
else
@ -153,14 +153,12 @@ end;
procedure TMVCStreamSerializerJsonDataObject.SerializeRoot(const AObject: TObject; out ASerializerObject: TObject;
const AAttributes: TArray<TCustomAttribute>; const ASerializationAction: TMVCSerializationAction = nil);
var
lSerializerObject: TJsonObject;
begin
lSerializerObject := TJsonObject.Create;
ASerializerObject := TJsonObject.Create;
try
SerializeAttribute(AObject, 'data', ASerializerObject, AAttributes);
except
lSerializerObject.Free;
ASerializerObject.Free;
raise;
end;
end;

View File

@ -1195,11 +1195,12 @@ begin
if AObject is TDataSet then
begin
raise Exception.Create('Not supported yet');
// Exit(self.SerializeDataSet(TDataSet(AObject)));
end;
if AObject is TJsonValue then
begin
Exit(TJDOJsonObject.Parse(TJsonValue(AObject).ToJSON) as TJDOJsonObject);
end;
ObjType := GetRttiContext.GetType(AObject.ClassType);

View File

@ -159,6 +159,11 @@ type
property Path: string read FPath;
end;
MVCInheritableAttribute = class(MVCBaseAttribute)
end;
TMVCWebRequest = class
private
FWebRequest: TWebRequest;

View File

@ -39,12 +39,8 @@
{$DEFINE MOBILE}
{$ENDIF}
{$IF CompilerVersion < 27} // XE5-
DelphiMVCFramework is compatible with Delphi version XE6 or better
{$ENDIF}
{$IF CompilerVersion >= 27} // XE6
{$DEFINE XE6ORBETTER}
{$IF CompilerVersion <= 27} // XE6
DelphiMVCFramework is compatible with Delphi version XE7 or better
{$ENDIF}
{$IF CompilerVersion >= 28} // XE7
@ -84,14 +80,14 @@ DelphiMVCFramework is compatible with Delphi version XE6 or better
{$ENDIF}
{$IFDEF XE6ORBETTER} // XE6
//{$IFDEF XE6ORBETTER} // XE6
{$DEFINE SYSTEMJSON}
{$DEFINE WEBAPACHEHTTP}
{$DEFINE USEFIREDAC}
{$ELSE}
{$IFDEF XE5ORBETTER} // XE5
{$DEFINE USEFIREDAC}
{$ELSE}
{$DEFINE USEDBX} // XE3, XE4
{$ENDIF}
{$ENDIF}
//{$ELSE}
//{$IFDEF XE5ORBETTER} // XE5
//{$DEFINE USEFIREDAC}
//{$ELSE}
//{$DEFINE USEDBX} // XE3, XE4
//{$ENDIF}
//{$ENDIF}