dmvcframework 3.2.0-boron

This commit is contained in:
Daniele Teti 2019-11-03 16:16:35 +01:00
parent 887a065b86
commit 7ce87edb51
18 changed files with 1364 additions and 472 deletions

View File

@ -167,7 +167,7 @@ end;
- New Sample! Server in DLL
- Improved! New constants defined in `HTTP_STATUS` to better describe the http status response.
- Improved! Now Firebird RQL' SQLGenerator can include primary key in `CreateInsert` if not autogenerated.
- New! Added support for `TArray<String>` and `TArray<Integer>` in default JSON serializer (Thank you [Pedro Oliveira](https://github.com/pedrooliveira01))
- New! Added support for `TArray<String>`, `TArray<Integer>` and `TArray<Double>` in default JSON serializer (Thank you [Pedro Oliveira](https://github.com/pedrooliveira01))
- Improved JWT Standard Compliance! Thanks to [Vinicius Sanchez](https://github.com/viniciussanchez) for his work on [issue #241](https://github.com/danieleteti/delphimvcframework/issues/241)
- Improved! DMVCFramework now has 130+ unit tests that checks its functionalities at each build!
- New! `StrToJSONObject` function to safely parse a string into a JSON object.
@ -207,7 +207,7 @@ end;
- ` procedure ResponseAccepted(const HREF: String; const ID: String; const Reason: String = 'Accepted'); virtual;`
- `procedure ResponseNoContent(const Reason: String = 'No Content'); virtual;`
- Added deserializing generic lists support without `MVCListOf` attribute (Thank you to [João Antônio Duarte](https://github.com/joaoduarte19)).
- Added de/serializing iterables (e.g. generic lists) support without `MVCListOf` attribute (Thank you to [João Antônio Duarte](https://github.com/joaoduarte19)).
It is now possible to deserialize a generic class like this:

View File

@ -63,7 +63,7 @@ type
constructor Create; virtual;
function GenerateJsonObject: TJSONObject; virtual; abstract;
procedure Load(pJson: TJSONObject); virtual; abstract;
procedure Load(pJson: TJSONObject); virtual;
/// <summary>
/// A single security scheme definition, mapping a "name" to the scheme it defines.
@ -153,6 +153,11 @@ begin
inherited Create;
end;
procedure TSwagSecurityDefinition.Load(pJson: TJSONObject);
begin
//do nothing
end;
function TSwagSecurityDefinition.ReturnTypeSecurityToString: string;
begin
Result := c_SwagSecurityDefinitionType[GetTypeSecurity];

View File

@ -81,7 +81,7 @@ begin
end);
FMVC.AddController(TPrivateController);
FMVC.AddMiddleware(
TMVCRoleBasedAuthenticationMiddleware.Create(
TMVCRoleBasedAuthMiddleware.Create(
TCustomRoleAuth.Create,
'/system/users/logged'
)

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{EDD78707-A0BE-4217-9B4E-919CCEDF5CF6}</ProjectGuid>
<ProjectVersion>18.6</ProjectVersion>
<ProjectVersion>18.7</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>custom_exception_handling.dpr</MainSource>
<Base>True</Base>
@ -43,6 +43,11 @@
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='OSX64' and '$(Base)'=='true') or '$(Base_OSX64)'!=''">
<Base_OSX64>true</Base_OSX64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
@ -91,6 +96,11 @@
<PropertyGroup Condition="'$(Base_Android)'!=''">
<DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;DataSnapFireDAC;tethering;bindcompfmx;FmxTeeUI;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage)</DCC_UsePackage>
<EnabledSysJars>android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar</EnabledSysJars>
<Android_NotificationIcon24>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png</Android_NotificationIcon24>
<Android_NotificationIcon36>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png</Android_NotificationIcon36>
<Android_NotificationIcon48>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png</Android_NotificationIcon48>
<Android_NotificationIcon72>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png</Android_NotificationIcon72>
<Android_NotificationIcon96>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png</Android_NotificationIcon96>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_iOSDevice32)'!=''">
<DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;DataSnapFireDAC;tethering;bindcompfmx;FmxTeeUI;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;TMSFMXCloudPackPkgDXE11;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
@ -107,6 +117,14 @@
<PropertyGroup Condition="'$(Base_OSX32)'!=''">
<DCC_UsePackage>DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;DataSnapFireDAC;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_OSX64)'!=''">
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts</VerInfo_Keys>
<BT_BuildType>Debug</BT_BuildType>
<Base_OSX32>true</Base_OSX32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
<DCC_UsePackage>DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;DataSnapFireDAC;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage);$(DCC_UsePackage)</DCC_UsePackage>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_UsePackage>DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;DataSnapFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;pfmgrPkgDXE11;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;inetdb;TMSCryptoPkgDXE11;FmxTeeUI;emsedge;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;pfcorePkgDXE11;FixInsight_10_2;DataSnapConnectors;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TMSFMXCloudPackPkgDXE11;TeeDB;emshosting;TMSFMXWebGMapsPkgDXE11;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;TMSFMXCloudPackPkgDEDXE11;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;TMSFMXPackPkgDXE11;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;TMSCryptoPkgDEDXE11;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
@ -259,6 +277,12 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
@ -295,6 +319,36 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon24">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage426">
<Platform Name="Android">
<RemoteDir>res\drawable-small</RemoteDir>
@ -319,6 +373,12 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Strings">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
@ -417,6 +477,17 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1024x768">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1536">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -428,6 +499,39 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1536x2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1668">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1668x2388">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -439,6 +543,61 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048x1536">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048x2732">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2224">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2388x1668">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2732x2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch768">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -450,6 +609,116 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch768x1024">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1125">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1136x640">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1242">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1242x2688">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1334">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1792">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2208">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2436">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2688x1242">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch320">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -483,6 +752,28 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch750">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch828">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
@ -597,6 +888,7 @@
<Platform value="iOSSimulator">False</Platform>
<Platform value="Linux64">False</Platform>
<Platform value="OSX32">False</Platform>
<Platform value="OSX64">False</Platform>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{7B54055A-5749-4136-9FE2-35FDBEEA874C}</ProjectGuid>
<ProjectVersion>18.6</ProjectVersion>
<ProjectVersion>18.7</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>JWTServer.dpr</MainSource>
<Base>True</Base>
@ -91,6 +91,11 @@
<BT_BuildType>Debug</BT_BuildType>
<AUP_CALL_PHONE>true</AUP_CALL_PHONE>
<AUP_ACCESS_FINE_LOCATION>true</AUP_ACCESS_FINE_LOCATION>
<Android_NotificationIcon24>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png</Android_NotificationIcon24>
<Android_NotificationIcon36>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png</Android_NotificationIcon36>
<Android_NotificationIcon48>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png</Android_NotificationIcon48>
<Android_NotificationIcon72>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png</Android_NotificationIcon72>
<Android_NotificationIcon96>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png</Android_NotificationIcon96>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
@ -265,6 +270,12 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
@ -301,6 +312,36 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon24">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage426">
<Platform Name="Android">
<RemoteDir>res\drawable-small</RemoteDir>
@ -325,6 +366,12 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Strings">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
@ -423,6 +470,17 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1024x768">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1536">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -434,6 +492,39 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1536x2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1668">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1668x2388">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -445,6 +536,61 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048x1536">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048x2732">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2224">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2388x1668">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2732x2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch768">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -456,6 +602,116 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch768x1024">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1125">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1136x640">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1242">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1242x2688">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1334">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1792">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2208">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2436">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2688x1242">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch320">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -489,6 +745,28 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch750">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch828">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{E7317702-64D3-4A65-8734-030F3AE3DBBC}</ProjectGuid>
<ProjectVersion>18.6</ProjectVersion>
<ProjectVersion>18.7</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>JWTClient.dpr</MainSource>
<Base>True</Base>
@ -211,6 +211,12 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
@ -247,6 +253,36 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon24">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage426">
<Platform Name="Android">
<RemoteDir>res\drawable-small</RemoteDir>
@ -271,6 +307,12 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Strings">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
@ -390,6 +432,17 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1024x768">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1536">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -401,6 +454,39 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1536x2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1668">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1668x2388">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -412,6 +498,61 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048x1536">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048x2732">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2224">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2388x1668">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2732x2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch768">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -423,6 +564,116 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch768x1024">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1125">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1136x640">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1242">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1242x2688">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1334">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1792">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2208">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2436">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2688x1242">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch320">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
@ -456,6 +707,28 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch750">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch828">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>

View File

@ -62,9 +62,9 @@ object MainForm: TMainForm
TabOrder = 2
object Label1: TLabel
AlignWithMargins = True
Left = 338
Left = 417
Top = 4
Width = 379
Width = 300
Height = 73
Align = alClient
Caption =
@ -74,12 +74,13 @@ object MainForm: TMainForm
'alidityWindowInSeconds seconds. It is useful to mimic the classi' +
'c session cookie with the semplicity of the JWT.'
WordWrap = True
ExplicitLeft = 338
ExplicitWidth = 368
ExplicitHeight = 65
end
object btnGet: TButton
AlignWithMargins = True
Left = 171
Left = 250
Top = 4
Width = 161
Height = 73
@ -88,17 +89,31 @@ object MainForm: TMainForm
TabOrder = 0
WordWrap = True
OnClick = btnGetClick
ExplicitLeft = 171
end
object btnLOGIN: TButton
AlignWithMargins = True
Left = 4
Top = 4
Width = 161
Width = 117
Height = 73
Align = alLeft
Caption = 'Login'
Caption = 'Login (Custom headers)'
TabOrder = 1
WordWrap = True
OnClick = btnLOGINClick
end
object btnLoginWithHeaderBasic: TButton
AlignWithMargins = True
Left = 127
Top = 4
Width = 117
Height = 73
Align = alLeft
Caption = 'Login (with Basic Schema)'
TabOrder = 2
WordWrap = True
OnClick = btnLoginWithHeaderBasicClick
end
end
end

View File

@ -16,8 +16,10 @@ type
btnLOGIN: TButton;
Splitter1: TSplitter;
Label1: TLabel;
btnLoginWithHeaderBasic: TButton;
procedure btnGetClick(Sender: TObject);
procedure btnLOGINClick(Sender: TObject);
procedure btnLoginWithHeaderBasicClick(Sender: TObject);
private
FJWT: string;
procedure SetJWT(const Value: string);
@ -33,13 +35,13 @@ implementation
{$R *.dfm}
uses
MVCFramework.RESTClient,
MVCFramework.Middleware.JWT,
MVCFramework.Serializer.JSONDataObjects,
MVCFramework.SystemJSONUtils,
System.JSON,
System.NetEncoding;
System.NetEncoding,
JsonDataObjects;
procedure TMainForm.btnGetClick(Sender: TObject);
var
@ -53,7 +55,9 @@ begin
try
lClient.ReadTimeOut(0);
if not FJWT.IsEmpty then
begin
lClient.RequestHeaders.Values[TMVCJWTDefaults.AUTHORIZATION_HEADER] := 'Bearer ' + FJWT;
end;
lQueryStringParams := TStringList.Create;
try
lQueryStringParams.Values['firstname'] := 'Daniele';
@ -61,7 +65,7 @@ begin
lResp := lClient.doGET('/admin/role1', [], lQueryStringParams);
if lResp.HasError then
ShowMessage(lResp.Error.ExceptionMessage);
ShowMessage(lResp.Error.Status + sLineBreak + lResp.Error.ExceptionMessage);
finally
lQueryStringParams.Free;
@ -90,13 +94,33 @@ begin
lClient := TRESTClient.Create('localhost', 8080);
try
lClient.ReadTimeOut(0);
lClient
.Header(TMVCJWTDefaults.USERNAME_HEADER, 'user1')
.Header(TMVCJWTDefaults.PASSWORD_HEADER, 'user1');
lRest := lClient.doPOST('/login', []);
lJSON := TSystemJSON.StringAsJSONObject(lRest.BodyAsString);
lClient.Header(TMVCJWTDefaults.USERNAME_HEADER, 'user1').Header(TMVCJWTDefaults.PASSWORD_HEADER, 'user1');
lRest := lClient.doGET('/login', []); { any HTTP verbs is OK }
lJSON := StrToJSONObject(lRest.BodyAsString);
try
JWT := lJSON.GetValue('token').Value;
JWT := lJSON.S['token'];
finally
lJSON.Free;
end;
finally
lClient.Free;
end;
end;
procedure TMainForm.btnLoginWithHeaderBasicClick(Sender: TObject);
var
lClient: TRESTClient;
lRest: IRESTResponse;
lJSON: TJSONObject;
begin
lClient := TRESTClient.Create('localhost', 8080);
try
lClient.ReadTimeOut(0);
lClient.Authentication('user1', 'user1');
lRest := lClient.doPOST('/login', []);
lJSON := StrToJSONObject(lRest.BodyAsString);
try
JWT := lJSON.S['token'];
finally
lJSON.Free;
end;

View File

@ -58,8 +58,22 @@ type
property RecordAlias: TNullableRecordAlias read fRecordAlias write fRecordAlias;
end;
TArrayTest = class
private
fStrings: TArray<String>;
fIntegers: TArray<Integer>;
fDoubles: TArray<Double>;
public
constructor Create;
property Integers: TArray<Integer> read fIntegers write fIntegers;
property Strings: TArray<String> read fStrings write fStrings;
property Doubles: TArray<Double> read fDoubles write fDoubles;
end;
implementation
uses
System.SysUtils, System.Math;
{ TSysUser }
@ -80,4 +94,28 @@ begin
FUserName := Value;
end;
{ TArrayTest }
constructor TArrayTest.Create;
var
I: Integer;
begin
inherited;
SetLength(fStrings, 5);
for I := 0 to Length(fStrings) - 1 do
begin
fStrings[I] := 'Value ' + I.ToString;
end;
SetLength(fIntegers, 5);
for I := 0 to Length(fIntegers) - 1 do
begin
fIntegers[I] := I;
end;
SetLength(fDoubles, 5);
for I := 0 to Length(fDoubles) - 1 do
begin
fDoubles[I] := Power(I, I) * 1.1;
end;
end;
end.

View File

@ -163,6 +163,10 @@ type
[MVCPath('/customserializationtype')]
procedure GetCustomSerializationType;
[MVCHTTPMethod([httpGET])]
[MVCPath('/simplearray')]
procedure GetSimpleArrays;
end;
implementation
@ -587,6 +591,11 @@ begin
Render<TPerson>(List);
end;
procedure TRenderSampleController.GetSimpleArrays;
begin
Render(TArrayTest.Create);
end;
procedure TRenderSampleController.GetPeopleAsCSV;
begin
ResponseStream.AppendLine('first_name;last_name;age');

View File

@ -99,10 +99,10 @@ begin
DMVC.SetViewEngine(TMVCMustacheViewEngine);
// Register a custom serializer for TUserRoles (is compatible only with the default serializer)
DMVC
.Serializers
.Items[TMVCMediaType.APPLICATION_JSON]
.RegisterTypeSerializer(TypeInfo(TUserRoles), TUserRolesSerializer.Create);
// DMVC
// .Serializers
// .Items[TMVCMediaType.APPLICATION_JSON]
// .RegisterTypeSerializer(TypeInfo(TUserRoles), TUserRolesSerializer.Create);
// You can check how this custom type serializer works
// calling http://localhost:8080/customserializationtype

View File

@ -11,6 +11,7 @@ uses
MVCFramework.Middleware.Authentication.RoleBasedAuthHandler;
type
[MVCNameCase(ncLowerCase)]
TAddress = class
private
@ -33,6 +34,9 @@ type
property Number: string read FNumber write FNumber;
end;
[MVCNameCase(ncLowerCase)]
TPhones = class(TObjectList<TPhone>)
end;
[MVCNameCase(ncLowerCase)]
TPerson = class
@ -42,7 +46,7 @@ type
FCountry: string;
FCode: Integer;
FAddress: TAddress;
FPhones: TObjectList<TPhone>;
FPhones: TPhones;
public
constructor Create;
destructor Destroy; override;
@ -55,27 +59,29 @@ type
property Age: Integer read FAge write FAge;
[MVCSwagJsonSchemaField('country', 'Nationality of the person', True, False)]
property Country: string read FCountry write FCountry;
// [MVCSwagJsonSchemaField(stObject, 'address', 'Address')]
property Address: TAddress read FAddress write FAddress;
// [MVCSwagJsonSchemaField(stArray, 'phones', 'Contact phones of the person', False, True)]
property Phones: TObjectList<TPhone> read FPhones write FPhones;
property Phones: TPhones read FPhones write FPhones;
end;
[MVCPath('/person')]
[MVCNameCase(ncLowerCase)]
TPeople = class(TObjectList<TPerson>)
end;
[MVCPath('/people')]
[MVCSwagAuthentication(atBasic)]
TMyController2 = class(TMVCController)
public
[MVCPath('')]
[MVCHTTPMethod([httpGET])]
[MVCSwagSummary('Person', 'List all persons')]
[MVCSwagSummary('People', 'List all persons', 'getPeople')]
[MVCSwagParam(plQuery, 'per_page', 'Items per page', ptInteger)]
[MVCSwagResponses(200, 'Success', TPerson, True)]
[MVCSwagResponses(500, 'Internal Server Error')]
procedure GetAllPerson;
procedure GetAllPeople;
[MVCPath('/($Id)')]
[MVCHTTPMethod([httpGET])]
[MVCSwagSummary('Person', 'List Persons by Id', '66e83aa7-d170-44a7-a502-8f25ddd2a18a')]
[MVCSwagSummary('People', 'List Persons by Id', 'getPersonById')]
[MVCSwagParam(plPath, 'Id', 'Person id', ptInteger)]
[MVCSwagResponses(200, 'Success', TPerson)]
[MVCSwagResponses(500, 'Internal Server Error')]
@ -83,7 +89,7 @@ type
[MVCPath('')]
[MVCHTTPMethod([httpPOST])]
[MVCSwagSummary('Person', 'Insert Person')]
[MVCSwagSummary('People', 'Insert Person', 'createPerson')]
[MVCSwagParam(plBody, 'entity', 'Person object', TPerson)]
[MVCSwagResponses(201, 'Created')]
[MVCSwagResponses(401, 'Requires Authentication')]
@ -94,12 +100,11 @@ type
implementation
uses
MVCFramework.Controllers.Register;
uses MVCFramework.Controllers.Register;
{ TMyController2 }
procedure TMyController2.GetAllPerson;
procedure TMyController2.GetAllPeople;
var
LPerson: TPerson;
LPersons: TObjectList<TPerson>;
@ -142,7 +147,7 @@ constructor TPerson.Create;
begin
inherited;
FAddress := TAddress.Create;
FPhones := TObjectList<TPhone>.Create;
FPhones := TPhones.Create;
end;
destructor TPerson.Destroy;

View File

@ -47,75 +47,54 @@ type
FListSep: Char;
public
constructor Create(const aRole: string); overload;
constructor Create(const aRole: string;
const aRoleEval: MVCRoleEval); overload;
constructor Create(const aRole: string; const aRoleEval: MVCRoleEval;
const aListSep: Char); overload;
constructor Create(const aRole: string; const aRoleEval: MVCRoleEval); overload;
constructor Create(const aRole: string; const aRoleEval: MVCRoleEval; const aListSep: Char); overload;
function GetRoles: TArray<string>;
property RoleEval: MVCRoleEval read FRoleEval;
end;
IMVCRoleBasedAuthenticationHandler = interface(IMVCAuthenticationHandler)
['{07ABEF93-DBCC-4C55-BD39-BD1F48490A73}']
// procedure OnAuthorization(const AContext: TWebContext;
// const AUserRoles: TList<string>;
// const AControllerQualifiedClassName: string; const AActionName: string;
// var AIsAuthorized: Boolean);
// procedure OnAuthorization(const AContext: TWebContext;
// const AUserRoles: TList<string>;
// const AControllerQualifiedClassName: string; const AActionName: string;
// var AIsAuthorized: Boolean);
end;
TRoleBasedAuthHandler = class(TInterfacedObject, IMVCAuthenticationHandler,
IMVCRoleBasedAuthenticationHandler)
TRoleBasedAuthHandler = class(TInterfacedObject, IMVCAuthenticationHandler, IMVCRoleBasedAuthenticationHandler)
private
FRttiContext: TRttiContext;
function TryGetAttributes<TTypeOfAttribute: TCustomAttribute>
(const aAttributes: TArray<TCustomAttribute>;
function TryGetAttributes<TTypeOfAttribute: TCustomAttribute>(const aAttributes: TArray<TCustomAttribute>;
out aListOfAttributes: TArray<TTypeOfAttribute>): Boolean;
function CheckUserRoles(const AContext: TWebContext;
const AUserRoles: TList<string>;
function CheckUserRoles(const AContext: TWebContext; const AUserRoles: TList<string>;
const aRoleAttributes: TArray<MVCRequiresRoleAttribute>): Boolean;
function ResolveRole(const AContext: TWebContext;
const aRole: string): string;
function ResolveRole(const AContext: TWebContext; const aRole: string): string;
function CreateParameterNameList(const aTemplate: string): TList<string>;
public
procedure OnRequest(const AContext: TWebContext; const ControllerQualifiedClassName: string;
const ActionName: string; var AuthenticationRequired: Boolean);
procedure OnAuthentication(const AContext: TWebContext; const UserName: string; const Password: string;
UserRoles: TList<string>; var IsValid: Boolean;
const SessionData: TDictionary<string, string>); virtual; abstract;
procedure OnAuthorization(
const AContext: TWebContext;
UserRoles: TList<string>;
const ControllerQualifiedClassName: string;
const ActionName: string;
var IsAuthorized: Boolean); overload;
// procedure OnAuthorization(const AContext: TWebContext;
// const AUserRoles: TList<string>;
// const AControllerQualifiedClassName: string; const AActionName: string;
// var AIsAuthorized: Boolean); overload;
UserRoles: TList<string>; var IsValid: Boolean; const SessionData: TDictionary<string, string>); virtual;
abstract;
procedure OnAuthorization(const AContext: TWebContext; UserRoles: TList<string>;
const ControllerQualifiedClassName: string; const ActionName: string; var IsAuthorized: Boolean); overload;
constructor Create;
destructor Destroy; override;
end;
TMVCRoleBasedAuthenticationMiddleware = class
(TMVCCustomAuthenticationMiddleware, IMVCMiddleware)
TMVCRoleBasedAuthMiddleware = class(TMVCCustomAuthenticationMiddleware, IMVCMiddleware)
private
FAuthenticationHandler: IMVCRoleBasedAuthenticationHandler;
procedure DoRoleBasedBeforeControllerAction(
const AContext: TWebContext;
const aHandler: IMVCRoleBasedAuthenticationHandler;
const AControllerQualifiedClassName: string; const AActionName: string;
var AHandled: Boolean);
fAuthenticationHandler: IMVCRoleBasedAuthenticationHandler;
procedure DoRoleBasedBeforeControllerAction(const AContext: TWebContext;
const aHandler: IMVCRoleBasedAuthenticationHandler; const AControllerQualifiedClassName: string;
const AActionName: string; var AHandled: Boolean);
protected
procedure OnBeforeControllerAction(AContext: TWebContext;
const AControllerQualifiedClassName: string; const AActionName: string;
var AHandled: Boolean);
procedure OnBeforeControllerAction(AContext: TWebContext; const AControllerQualifiedClassName: string;
const AActionName: string; var AHandled: Boolean);
public
constructor Create(const AAuthenticationHandler: IMVCAuthenticationHandler;
const ALoginUrl: string = '/system/users/logged'); override;
@ -134,14 +113,12 @@ begin
Self.Create(aRole, MVCRoleEval.reAND, DefaultListSeparator);
end;
constructor MVCRequiresRoleAttribute.Create(const aRole: string;
const aRoleEval: MVCRoleEval);
constructor MVCRequiresRoleAttribute.Create(const aRole: string; const aRoleEval: MVCRoleEval);
begin
Self.Create(aRole, aRoleEval, DefaultListSeparator);
end;
constructor MVCRequiresRoleAttribute.Create(const aRole: string;
const aRoleEval: MVCRoleEval; const aListSep: Char);
constructor MVCRequiresRoleAttribute.Create(const aRole: string; const aRoleEval: MVCRoleEval; const aListSep: Char);
begin
inherited Create;
FRole := aRole;
@ -156,8 +133,7 @@ end;
{ TRoleBasedAuthHandler }
function TRoleBasedAuthHandler.CheckUserRoles(const AContext: TWebContext;
const AUserRoles: TList<System.string>;
function TRoleBasedAuthHandler.CheckUserRoles(const AContext: TWebContext; const AUserRoles: TList<System.string>;
const aRoleAttributes: TArray<MVCRequiresRoleAttribute>): Boolean;
var
vAttribute: MVCRequiresRoleAttribute;
@ -199,8 +175,7 @@ begin
FRttiContext := TRttiContext.Create;
end;
function TRoleBasedAuthHandler.CreateParameterNameList(const aTemplate: string)
: TList<string>;
function TRoleBasedAuthHandler.CreateParameterNameList(const aTemplate: string): TList<string>;
const
MatchPattern = '\(\$([A-Za-z0-9\_]+)\)'; // Matches ($<name>) placeholders
var
@ -211,8 +186,7 @@ var
begin
Result := TList<string>.Create;
try
Matches := TRegEx.Matches(aTemplate, MatchPattern,
[roIgnoreCase, roCompiled, roSingleLine]);
Matches := TRegEx.Matches(aTemplate, MatchPattern, [roIgnoreCase, roCompiled, roSingleLine]);
for M in Matches do
for I := 0 to M.Groups.Count - 1 do
begin
@ -235,12 +209,8 @@ begin
inherited;
end;
procedure TRoleBasedAuthHandler.OnAuthorization(
const AContext: TWebContext;
UserRoles: TList<string>;
const ControllerQualifiedClassName: string;
const ActionName: string;
var IsAuthorized: Boolean);
procedure TRoleBasedAuthHandler.OnAuthorization(const AContext: TWebContext; UserRoles: TList<string>;
const ControllerQualifiedClassName: string; const ActionName: string; var IsAuthorized: Boolean);
var
vRttiType: TRttiType;
vAttributes: TArray<MVCRequiresRoleAttribute>;
@ -251,8 +221,7 @@ begin
// Check all Role requirements on the controller level
vRttiType := FRttiContext.FindType(ControllerQualifiedClassName);
if TryGetAttributes<MVCRequiresRoleAttribute>(vRttiType.GetAttributes,
vAttributes) then
if TryGetAttributes<MVCRequiresRoleAttribute>(vRttiType.GetAttributes, vAttributes) then
if not CheckUserRoles(AContext, UserRoles, vAttributes) then
begin
IsAuthorized := False;
@ -263,8 +232,7 @@ begin
// we have successfully cleared these.
// Verify all roles on the Action.
vRttiMethod := vRttiType.GetMethod(ActionName);
if TryGetAttributes<MVCRequiresRoleAttribute>(vRttiMethod.GetAttributes,
vAttributes) then
if TryGetAttributes<MVCRequiresRoleAttribute>(vRttiMethod.GetAttributes, vAttributes) then
if not CheckUserRoles(AContext, UserRoles, vAttributes) then
begin
IsAuthorized := False;
@ -273,7 +241,7 @@ begin
end;
procedure TRoleBasedAuthHandler.OnRequest(const AContext: TWebContext; const ControllerQualifiedClassName: string;
const ActionName: string; var AuthenticationRequired: Boolean);
const ActionName: string; var AuthenticationRequired: Boolean);
var
vRttiType: TRttiType;
vAttributes: TArray<MVCRequiresAuthenticationAttribute>;
@ -282,19 +250,16 @@ begin
vRttiType := FRttiContext.FindType(ControllerQualifiedClassName);
// Check class and Actions if they have role definitions.
AuthenticationRequired := TryGetAttributes<MVCRequiresAuthenticationAttribute>
(vRttiType.GetAttributes, vAttributes);
AuthenticationRequired := TryGetAttributes<MVCRequiresAuthenticationAttribute>(vRttiType.GetAttributes, vAttributes);
if not AuthenticationRequired then
begin
vRttiMethod := vRttiType.GetMethod(ActionName);
AuthenticationRequired :=
TryGetAttributes<MVCRequiresAuthenticationAttribute>
(vRttiMethod.GetAttributes, vAttributes);
AuthenticationRequired := TryGetAttributes<MVCRequiresAuthenticationAttribute>(vRttiMethod.GetAttributes,
vAttributes);
end;
end;
function TRoleBasedAuthHandler.ResolveRole(const AContext: TWebContext;
const aRole: string): string;
function TRoleBasedAuthHandler.ResolveRole(const AContext: TWebContext; const aRole: string): string;
var
vPlaceholders: TList<string>;
vPlaceholder: string;
@ -310,15 +275,14 @@ begin
Exit;
for vPlaceholder in vPlaceholders do
Result := Result.Replace(Format('($%s)', [vPlaceholder]),
AContext.ParamsTable.Items[vPlaceholder], [rfReplaceAll]);
Result := Result.Replace(Format('($%s)', [vPlaceholder]), AContext.ParamsTable.Items[vPlaceholder],
[rfReplaceAll]);
finally
vPlaceholders.Free;
end;
end;
function TRoleBasedAuthHandler.TryGetAttributes<TTypeOfAttribute>
(const aAttributes: TArray<TCustomAttribute>;
function TRoleBasedAuthHandler.TryGetAttributes<TTypeOfAttribute>(const aAttributes: TArray<TCustomAttribute>;
out aListOfAttributes: TArray<TTypeOfAttribute>): Boolean;
var
vAttribute: TCustomAttribute;
@ -342,23 +306,18 @@ begin
end;
end;
{ TMVCRoleBasedAuthenticationMiddleware }
{ TMVCRoleBasedAuthMiddleware }
constructor TMVCRoleBasedAuthenticationMiddleware.Create
(const AAuthenticationHandler: IMVCAuthenticationHandler;
constructor TMVCRoleBasedAuthMiddleware.Create(const AAuthenticationHandler: IMVCAuthenticationHandler;
const ALoginUrl: string);
begin
inherited Create(AAuthenticationHandler, ALoginUrl);
Supports(AAuthenticationHandler, IMVCRoleBasedAuthenticationHandler,
FAuthenticationHandler);
Supports(AAuthenticationHandler, IMVCRoleBasedAuthenticationHandler, fAuthenticationHandler);
end;
procedure TMVCRoleBasedAuthenticationMiddleware.
DoRoleBasedBeforeControllerAction(
const AContext: TWebContext;
const aHandler: IMVCRoleBasedAuthenticationHandler;
const AControllerQualifiedClassName: string; const AActionName: string;
var AHandled: Boolean);
procedure TMVCRoleBasedAuthMiddleware.DoRoleBasedBeforeControllerAction(const AContext: TWebContext;
const aHandler: IMVCRoleBasedAuthenticationHandler; const AControllerQualifiedClassName: string;
const AActionName: string; var AHandled: Boolean);
var
IsValid: Boolean;
IsAuthorized: Boolean;
@ -366,7 +325,6 @@ var
begin
// This procedure is a basic copy of the inherited OnBeforeControllerAction procedure.
// Extention is by enabling the Authorization based on the context the call is being performed.
aHandler.OnRequest(nil, AControllerQualifiedClassName, AActionName, AuthRequired);
if not AuthRequired then
begin
@ -388,31 +346,40 @@ begin
// Modification here from:
// FAuthenticationHandler.OnAuthorization(AContext.LoggedUser.Roles, AControllerQualifiedClassName, AActionName, IsAuthorized);
// to:
aHandler.OnAuthorization(AContext, AContext.LoggedUser.Roles,
AControllerQualifiedClassName, AActionName, IsAuthorized);
aHandler.OnAuthorization(AContext, AContext.LoggedUser.Roles, AControllerQualifiedClassName, AActionName,
IsAuthorized);
// Modification end
if IsAuthorized then
AHandled := False
begin
AHandled := False;
end
else
begin
if IsValid then
begin
SendResponse(AContext, AHandled, HTTP_STATUS.Forbidden)
end
else
begin
SendResponse(AContext, AHandled, HTTP_STATUS.Unauthorized);
end;
end;
end;
procedure TMVCRoleBasedAuthenticationMiddleware.OnBeforeControllerAction
(AContext: TWebContext; const AControllerQualifiedClassName,
AActionName: string; var AHandled: Boolean);
procedure TMVCRoleBasedAuthMiddleware.OnBeforeControllerAction(AContext: TWebContext;
const AControllerQualifiedClassName, AActionName: string; var AHandled: Boolean);
begin
if Assigned(FAuthenticationHandler) then
DoRoleBasedBeforeControllerAction(AContext, FAuthenticationHandler,
AControllerQualifiedClassName, AActionName, AHandled)
if Assigned(fAuthenticationHandler) then
begin
DoRoleBasedBeforeControllerAction(AContext, fAuthenticationHandler, AControllerQualifiedClassName,
AActionName, AHandled)
end
else
inherited OnBeforeControllerAction(AContext, AControllerQualifiedClassName,
AActionName, AHandled);
begin
inherited OnBeforeControllerAction(AContext, AControllerQualifiedClassName, AActionName, AHandled);
end;
end;
end.

View File

@ -52,8 +52,7 @@ type
procedure InternalRender(AContent: string; AContext: TWebContext);
public
constructor Create(const AEngine: TMVCEngine; const ASwaggerInfo: TMVCSwaggerInfo;
const ASwaggerDocumentationURL: string = '/swagger.json';
const AJWTDescription: string = JWT_DEFAULT_DESCRIPTION;
const ASwaggerDocumentationURL: string = '/swagger.json'; const AJWTDescription: string = JWT_DEFAULT_DESCRIPTION;
const AEnableBasicAuthentication: Boolean = False);
destructor Destroy; override;
procedure OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
@ -76,7 +75,7 @@ uses
MVCFramework.Middleware.JWT,
Swag.Doc.Path.Operation.RequestParameter,
Swag.Doc.SecurityDefinitionApiKey,
Swag.Doc.SecurityDefinitionBasic;
Swag.Doc.SecurityDefinitionBasic, Swag.Doc.Definition;
{ TMVCSwaggerMiddleware }
@ -176,10 +175,10 @@ begin
begin
LSwagPathOp := TSwagPathOperation.Create;
TMVCSwagger.FillOperationSummary(LSwagPathOp, LMethod);
if TMVCSwagger.MethodRequiresAuthentication(LMethod, LObjType, LAuthTypeName) then
begin
LSwagPathOp.Security.Add(LAuthTypeName);
end;
LSwagPathOp.Parameters.AddRange(TMVCSwagger.GetParamsFromMethod(LSwagPath.Uri, LMethod));
LSwagPathOp.Operation := TMVCSwagger.MVCHttpMethodToSwagPathOperation(I);
LSwagPath.Operations.Add(LSwagPathOp);

View File

@ -392,6 +392,8 @@ begin
AJsonObject.A[AName].Add(AValue.GetArrayElement(I).AsInteger);
tkInt64:
AJsonObject.A[AName].Add(AValue.GetArrayElement(I).AsInt64);
tkFloat:
AJsonObject.A[AName].Add(AValue.GetArrayElement(I).AsExtended);
else
raise EMVCSerializationException.CreateFmt
('Cannot serialize property or field "%s" of TypeKind tkArray or tkDynArray.', [AName]);

View File

@ -33,19 +33,20 @@ uses
Json.Schema,
System.Rtti,
Swag.Common.Types,
System.SysUtils,
MVCFramework.Commons,
Swag.Doc.Path.Operation.RequestParameter,
Swag.Doc.Path.Operation,
Swag.Doc.Path,
System.JSON,
System.Json,
Json.Schema.Field,
Json.Schema.Field.Objects;
Json.Schema.Field.Objects, Swag.Doc.Definition;
type
TMVCSwagParamLocation = (plNotDefined, plQuery, plHeader, plPath, plFormData, plBody);
TMVCSwagParamType = (ptNotDefined, ptString, ptNumber, ptInteger, ptBoolean, ptArray, ptFile);
TMVCSwagSchemaType = (stUnknown, stInteger, stInt64, stNumber, stDateTime, stDate, stTime,
stEnumeration, stBoolean, stObject, stArray, stString, stChar, stGuid);
TMVCSwagSchemaType = (stUnknown, stInteger, stInt64, stNumber, stDateTime, stDate, stTime, stEnumeration, stBoolean,
stObject, stArray, stString, stChar, stGuid);
TMVCSwagAuthenticationType = (atBasic, atJsonWebToken);
/// <summary>
@ -69,20 +70,21 @@ type
/// </summary>
MVCSwagSummaryAttribute = class(TCustomAttribute)
private
FTags: string;
FDeprecated: Boolean;
FDescription: string;
FOperationID: string;
fTags: string;
fDeprecated: Boolean;
fDescription: string;
fOperationID: string;
public
constructor Create(const ATags, ADescription: string; const APathId: string = ''; ADeprecated: Boolean = False);
constructor Create(const ATags, ADescription: string; const AOperationId: string = '';
ADeprecated: Boolean = False);
function GetTags: TArray<string>;
property Tags: string read FTags;
property Description: string read FDescription;
property OperationID: string read FOperationID;
property Deprecated: Boolean read FDeprecated;
property Tags: string read fTags;
property Description: string read fDescription;
property OperationID: string read fOperationID;
property Deprecated: Boolean read fDeprecated;
end;
/// <summary>
/// <summary>
/// Specifies your controller authentication type
/// </summary>
MVCSwagAuthenticationAttribute = class(TCustomAttribute)
@ -98,30 +100,23 @@ type
/// </summary>
MVCSwagResponsesAttribute = class(TCustomAttribute)
private
FStatusCode: Integer;
FDescription: string;
FJsonSchema: string;
FJsonSchemaClass: TClass;
FIsArray: Boolean;
fStatusCode: Integer;
fDescription: string;
fJsonSchema: string;
fJsonSchemaClass: TClass;
fIsArray: Boolean;
public
constructor Create(
const AStatusCode: Integer;
const ADescription: string;
const AJsonSchema: string = ''
); overload;
constructor Create(const AStatusCode: Integer; const ADescription: string; const AJsonSchema: string = '');
overload;
constructor Create(
const AStatusCode: Integer;
const ADescription: string;
const AJsonSchemaClass: TClass;
const AIsArray: Boolean = False
); overload;
constructor Create(const AStatusCode: Integer; const ADescription: string; const AJsonSchemaClass: TClass;
const AIsArray: Boolean = False); overload;
property StatusCode: Integer read FStatusCode;
property Description: string read FDescription;
property JsonSchema: string read FJsonSchema;
property JsonSchemaClass: TClass read FJsonSchemaClass;
property IsArray: Boolean read FIsArray;
property StatusCode: Integer read fStatusCode;
property Description: string read fDescription;
property JsonSchema: string read fJsonSchema;
property JsonSchemaClass: TClass read fJsonSchemaClass;
property IsArray: Boolean read fIsArray;
end;
/// <summary>
@ -129,68 +124,51 @@ type
/// </summary>
MVCSwagParamAttribute = class(TCustomAttribute)
private
FParamLocation: TMVCSwagParamLocation;
FParamName: string;
FParamDescription: string;
FParamType: TMVCSwagParamType;
FRequired: Boolean;
FJsonSchema: string;
FJsonSchemaClass: TClass;
fParamLocation: TMVCSwagParamLocation;
fParamName: string;
fParamDescription: string;
fParamType: TMVCSwagParamType;
fRequired: Boolean;
fJsonSchema: string;
fJsonSchemaClass: TClass;
public
constructor Create(
const AParamLocation: TMVCSwagParamLocation;
const AParamName: string;
const AParamDescription: string;
const AParamType: TMVCSwagParamType;
const ARequired: Boolean = True;
constructor Create(const AParamLocation: TMVCSwagParamLocation; const AParamName: string;
const AParamDescription: string; const AParamType: TMVCSwagParamType; const ARequired: Boolean = True;
const AJsonSchema: string = ''); overload;
constructor Create(
const AParamLocation: TMVCSwagParamLocation;
const AParamName: string;
const AParamDescription: string;
const AJsonSchemaClass: TClass;
const AParamType: TMVCSwagParamType = ptNotDefined;
const ARequired: Boolean = True); overload;
constructor Create(const AParamLocation: TMVCSwagParamLocation; const AParamName: string;
const AParamDescription: string; const AJsonSchemaClass: TClass;
const AParamType: TMVCSwagParamType = ptNotDefined; const ARequired: Boolean = True); overload;
property ParamLocation: TMVCSwagParamLocation read FParamLocation;
property ParamName: string read FParamName;
property ParamDescription: string read FParamDescription;
property ParamType: TMVCSwagParamType read FParamType;
property Required: Boolean read FRequired;
property JsonSchema: string read FJsonSchema;
property JsonSchemaClass: TClass read FJsonSchemaClass;
property ParamLocation: TMVCSwagParamLocation read fParamLocation;
property ParamName: string read fParamName;
property ParamDescription: string read fParamDescription;
property ParamType: TMVCSwagParamType read fParamType;
property Required: Boolean read fRequired;
property JsonSchema: string read fJsonSchema;
property JsonSchemaClass: TClass read fJsonSchemaClass;
end;
/// <summary>
/// Specifies the field definition in the json schema.
/// Use this attribute on a class property that will be mapped to a json schema
/// </summary>
MVCSwagJsonSchemaFieldAttribute = class(TCustomAttribute)
MVCSwagJSONSchemaFieldAttribute = class(TCustomAttribute)
private
FSchemaFieldType: TMVCSwagSchemaType;
FFieldName: string;
FDescription: string;
FRequired: Boolean;
fDescription: string;
fRequired: Boolean;
FNullable: Boolean;
public
constructor Create(
const ASchemaFieldType: TMVCSwagSchemaType;
const AFieldName: string;
const ADescription: string;
const ARequired: Boolean = True;
const ANullable: Boolean = False
); overload;
constructor Create(
const AFieldName: string;
const ADescription: string;
const ARequired: Boolean = True;
const ANullable: Boolean = False
); overload;
constructor Create(const ASchemaFieldType: TMVCSwagSchemaType; const AFieldName: string; const ADescription: string;
const ARequired: Boolean = True; const ANullable: Boolean = False); overload;
constructor Create(const AFieldName: string; const ADescription: string; const ARequired: Boolean = True;
const ANullable: Boolean = False); overload;
property SchemaFieldType: TMVCSwagSchemaType read FSchemaFieldType;
property FieldName: string read FFieldName;
property Description: string read FDescription;
property Required: Boolean read FRequired;
property Description: string read fDescription;
property Required: Boolean read fRequired;
property Nullable: Boolean read FNullable;
end;
@ -201,22 +179,22 @@ type
private
class var FRttiContext: TRttiContext;
class function GetMVCSwagParamsFromMethod(const AMethod: TRttiMethod): TArray<MVCSwagParamAttribute>;
class function MVCParamLocationToSwagRequestParamInLocation(const AMVCSwagParamLocation: TMVCSwagParamLocation):
TSwagRequestParameterInLocation;
class function MVCParamLocationToSwagRequestParamInLocation(const AMVCSwagParamLocation: TMVCSwagParamLocation)
: TSwagRequestParameterInLocation;
class function MVCParamTypeToSwagTypeParameter(const AMVSwagParamType: TMVCSwagParamType): TSwagTypeParameter;
class procedure ExtractJsonSchemaFromClass(const AJsonFieldRoot: TJsonFieldObject; const AClass: TClass); overload;
class function ExtractJsonSchemaFromClass(const AClass: TClass; const AIsArray: Boolean = False)
: TJSONObject; overload;
class function GetJsonFieldClass(const ASchemaFieldType: TMVCSwagSchemaType): TJsonFieldClass;
class function TypeKindToMVCSwagSchemaType(APropType: TRttiType): TMVCSwagSchemaType; static;
class function RttiTypeIsObjectList(const ARttiType: TRttiType): Boolean; static;
class function TypeIsEnumerable(const ARttiType: TRttiType): Boolean; static;
public
class constructor Create;
class destructor Destroy;
class function MVCHttpMethodToSwagPathOperation(const AMVCHTTPMethod: TMVCHTTPMethodType): TSwagPathTypeOperation;
class function MVCPathToSwagPath(const AResourcePath: string): string;
class function GetParamsFromMethod(const AResourcePath: string; const AMethod: TRttiMethod):
TArray<TSwagRequestParameter>;
class function GetParamsFromMethod(const AResourcePath: string; const AMethod: TRttiMethod)
: TArray<TSwagRequestParameter>;
class function RttiTypeToSwagType(const ARttiType: TRttiType): TSwagTypeParameter;
class procedure FillOperationSummary(const ASwagPathOperation: TSwagPathOperation; const AMethod: TRttiMethod);
class function MethodRequiresAuthentication(const AMethod: TRttiMethod; const AType: TRttiType;
@ -227,16 +205,9 @@ type
const
SECURITY_BEARER_NAME = 'bearer';
SECURITY_BASIC_NAME = 'basic';
JWT_JSON_SCHEMA =
'{' + sLineBreak +
' "type": "object",' + sLineBreak +
' "properties": {' + sLineBreak +
' "token": {' + sLineBreak +
' "type": "string",' + sLineBreak +
' "description": "JWT Token"' + sLineBreak +
' }' + sLineBreak +
' }' + sLineBreak +
'}';
JWT_JSON_SCHEMA = '{' + sLineBreak + ' "type": "object",' + sLineBreak + ' "properties": {' + sLineBreak +
' "token": {' + sLineBreak + ' "type": "string",' + sLineBreak + ' "description": "JWT Token"' +
sLineBreak + ' }' + sLineBreak + ' }' + sLineBreak + '}';
JWT_DEFAULT_DESCRIPTION = 'For accessing the API a valid JWT token must be passed in all the queries ' +
'in the ''Authorization'' header.' + sLineBreak + sLineBreak +
'A valid JWT token is generated by the API and retourned as answer of a call ' + 'to the route defined ' +
@ -249,11 +220,9 @@ implementation
uses
System.Classes,
System.RegularExpressions,
System.SysUtils,
MVCFramework,
MVCFramework.Serializer.Abstract,
MVCFramework.Serializer.Commons,
MVCFramework.Middleware.Authentication.RoleBasedAuthHandler,
Swag.Doc.Path.Operation.Response,
Json.Schema.Field.Numbers,
Json.Schema.Field.Strings,
@ -277,8 +246,7 @@ begin
tkChar, tkString, tkWChar, tkLString, tkWString, tkUString:
Result := stpString;
tkFloat:
if (ARttiType.Handle = TypeInfo(TDateTime)) or
(ARttiType.Handle = TypeInfo(TDate)) or
if (ARttiType.Handle = TypeInfo(TDateTime)) or (ARttiType.Handle = TypeInfo(TDate)) or
(ARttiType.Handle = TypeInfo(TTime)) then
Result := stpString
else
@ -298,8 +266,7 @@ begin
FRttiContext.Free;
end;
class function TMVCSwagger.RttiTypeIsObjectList(const ARttiType: TRttiType): Boolean;
class function TMVCSwagger.TypeIsEnumerable(const ARttiType: TRttiType): Boolean;
begin
Result := ARttiType.GetMethod('GetEnumerator') <> nil;
end;
@ -318,154 +285,169 @@ type
class procedure TMVCSwagger.ExtractJsonSchemaFromClass(const AJsonFieldRoot: TJsonFieldObject; const AClass: TClass);
var
LFieldSchemaDef: TFieldSchemaDefinition;
LObjType: TRttiType;
LProp: TRttiProperty;
LSkipProp: Boolean;
LAttr: TCustomAttribute;
LJSFieldAttr: MVCSwagJsonSchemaFieldAttribute;
LJsonFieldClass: TJsonFieldClass;
LJsonField: TJsonField;
LJsonFieldObject: TJsonFieldObject;
LAbstractSer: THackMVCAbstractSerializer;
LClass: TClass;
lFieldSchemaDef: TFieldSchemaDefinition;
lObjType: TRttiType;
lProp: TRttiProperty;
lSkipProp: Boolean;
lAttr: TCustomAttribute;
lJSFieldAttr: MVCSwagJSONSchemaFieldAttribute;
lJsonFieldClass: TJsonFieldClass;
lJsonField: TJsonField;
lJsonFieldObject: TJsonFieldObject;
lAbstractSer: THackMVCAbstractSerializer;
lClass: TClass;
begin
LObjType := FRttiContext.GetType(AClass);
for LProp in LObjType.GetProperties do
lObjType := FRttiContext.GetType(AClass);
for lProp in lObjType.GetProperties do
begin
LSkipProp := False;
LFieldSchemaDef := TFieldSchemaDefinition.Create;
lSkipProp := False;
lFieldSchemaDef := TFieldSchemaDefinition.Create;
for LAttr in LProp.GetAttributes do
for lAttr in lProp.GetAttributes do
begin
if LAttr is MVCDoNotSerializeAttribute then
if lAttr is MVCDoNotSerializeAttribute then
begin
LSkipProp := True;
lSkipProp := True;
Break;
end;
if LAttr is MVCSwagJsonSchemaFieldAttribute then
if lAttr is MVCSwagJSONSchemaFieldAttribute then
begin
LJSFieldAttr := MVCSwagJsonSchemaFieldAttribute(LAttr);
LFieldSchemaDef.SchemaFieldType := LJSFieldAttr.SchemaFieldType;
LFieldSchemaDef.FieldName := LJSFieldAttr.FieldName;
LFieldSchemaDef.Description := LJSFieldAttr.Description;
LFieldSchemaDef.Required := LJSFieldAttr.Required;
LFieldSchemaDef.Nullable := LJSFieldAttr.Nullable;
lJSFieldAttr := MVCSwagJSONSchemaFieldAttribute(lAttr);
lFieldSchemaDef.SchemaFieldType := lJSFieldAttr.SchemaFieldType;
lFieldSchemaDef.FieldName := lJSFieldAttr.FieldName;
lFieldSchemaDef.Description := lJSFieldAttr.Description;
lFieldSchemaDef.Required := lJSFieldAttr.Required;
lFieldSchemaDef.Nullable := lJSFieldAttr.Nullable;
Break;
end;
end;
if LSkipProp then
if lSkipProp then
Continue;
if LFieldSchemaDef.SchemaFieldType = stUnknown then
if lFieldSchemaDef.SchemaFieldType = stUnknown then
begin
LFieldSchemaDef.SchemaFieldType := TypeKindToMVCSwagSchemaType(LProp.PropertyType);
LFieldSchemaDef.FieldName := TMVCSerializerHelper.GetKeyName(LProp, LObjType);
lFieldSchemaDef.SchemaFieldType := TypeKindToMVCSwagSchemaType(lProp.PropertyType);
lFieldSchemaDef.FieldName := TMVCSerializerHelper.GetKeyName(lProp, lObjType);
end;
LJsonFieldClass := GetJsonFieldClass(LFieldSchemaDef.SchemaFieldType);
if not Assigned(LJsonFieldClass) then
lJsonFieldClass := GetJsonFieldClass(lFieldSchemaDef.SchemaFieldType);
if not Assigned(lJsonFieldClass) then
Continue;
LJsonField := LJsonFieldClass.Create;
lJsonField := lJsonFieldClass.Create;
if (LJsonField is TJsonFieldObject) and (not RttiTypeIsObjectList(LProp.PropertyType)) then
if (lJsonField is TJsonFieldObject) and (not TypeIsEnumerable(lProp.PropertyType)) then
begin
ExtractJsonSchemaFromClass((LJsonField as TJsonFieldObject), LProp.PropertyType.AsInstance.MetaClassType);
ExtractJsonSchemaFromClass((lJsonField as TJsonFieldObject), lProp.PropertyType.AsInstance.MetaClassType);
end;
if (LJsonField is TJsonFieldArray) and RttiTypeIsObjectList(LProp.PropertyType) then
if (lJsonField is TJsonFieldArray) and TypeIsEnumerable(lProp.PropertyType) then
begin
LJsonFieldObject := TJsonFieldObject.Create;
lJsonFieldObject := TJsonFieldObject.Create;
LAbstractSer := THackMVCAbstractSerializer.Create;
lAbstractSer := THackMVCAbstractSerializer.Create;
try
LClass := LAbstractSer.GetObjectTypeOfGenericList(LProp.PropertyType.Handle);
ExtractJsonSchemaFromClass(LJsonFieldObject, LClass);
(LJsonField as TJsonFieldArray).ItemFieldType := LJsonFieldObject;
lClass := lAbstractSer.GetObjectTypeOfGenericList(lProp.PropertyType.Handle);
ExtractJsonSchemaFromClass(lJsonFieldObject, lClass);
(lJsonField as TJsonFieldArray).ItemFieldType := lJsonFieldObject;
finally
LAbstractSer.Free;
lAbstractSer.Free;
end;
end;
LJsonField.Name := LFieldSchemaDef.FieldName;
LJsonField.Required := LFieldSchemaDef.Required;
LJsonField.Nullable := LFieldSchemaDef.Nullable;
if not LFieldSchemaDef.Description.IsEmpty then
TJsonFieldInteger(LJsonField).Description := LFieldSchemaDef.Description;
lJsonField.Name := lFieldSchemaDef.FieldName;
lJsonField.Required := lFieldSchemaDef.Required;
lJsonField.Nullable := lFieldSchemaDef.Nullable;
if not lFieldSchemaDef.Description.IsEmpty then
TJsonFieldInteger(lJsonField).Description := lFieldSchemaDef.Description;
AJsonFieldRoot.AddField(LJsonField);
AJsonFieldRoot.AddField(lJsonField);
end;
end;
class function TMVCSwagger.ExtractJsonSchemaFromClass(const AClass: TClass; const AIsArray: Boolean): TJSONObject;
var
LJsonSchema: TJsonField;
LJsonRoot: TJsonFieldObject;
lJsonSchema: TJsonField;
lJsonRoot: TJsonFieldObject;
begin
var
lClassName := AClass.ClassName;
if AIsArray then
LJsonSchema := TJsonFieldArray.Create
begin
lJsonSchema := TJsonFieldArray.Create
end
else
LJsonSchema := TJsonFieldObject.Create;
begin
lJsonSchema := TJsonFieldObject.Create;
end;
try
if AIsArray then
begin
LJsonRoot := TJsonFieldObject.Create;
TJsonFieldArray(LJsonSchema).ItemFieldType := LJsonRoot;
TJsonFieldArray(LJsonSchema).Name := 'items';
lJsonRoot := TJsonFieldObject.Create;
TJsonFieldArray(lJsonSchema).ItemFieldType := lJsonRoot;
TJsonFieldArray(lJsonSchema).Name := 'items';
end
else
begin
LJsonRoot := LJsonSchema as TJsonFieldObject;
lJsonRoot := lJsonSchema as TJsonFieldObject;
end;
ExtractJsonSchemaFromClass(LJsonRoot, AClass);
Result := LJsonSchema.ToJsonSchema;
ExtractJsonSchemaFromClass(lJsonRoot, AClass);
Result := lJsonSchema.ToJsonSchema;
finally
LJsonSchema.Free;
lJsonSchema.Free;
end;
end;
class procedure TMVCSwagger.FillOperationSummary(const ASwagPathOperation: TSwagPathOperation;
const AMethod: TRttiMethod);
var
LAttr: TCustomAttribute;
LSwagResponse: TSwagResponse;
LSwagResponsesAttr: MVCSwagResponsesAttribute;
lAttr: TCustomAttribute;
lSwagResponse: TSwagResponse;
lSwagResponsesAttr: MVCSwagResponsesAttribute;
begin
for LAttr in AMethod.GetAttributes do
for lAttr in AMethod.GetAttributes do
begin
if LAttr is MVCSwagSummaryAttribute then
if lAttr is MVCSwagSummaryAttribute then
begin
ASwagPathOperation.Tags.AddRange(MVCSwagSummaryAttribute(LAttr).GetTags);
ASwagPathOperation.Description := MVCSwagSummaryAttribute(LAttr).Description;
ASwagPathOperation.OperationId := MVCSwagSummaryAttribute(LAttr).OperationID;
ASwagPathOperation.Deprecated := MVCSwagSummaryAttribute(LAttr).Deprecated;
ASwagPathOperation.Tags.AddRange(MVCSwagSummaryAttribute(lAttr).GetTags);
ASwagPathOperation.Description := MVCSwagSummaryAttribute(lAttr).Description;
ASwagPathOperation.OperationID := MVCSwagSummaryAttribute(lAttr).OperationID;
// dt [2019-10-31] Ensure OperationID is always defined
if ASwagPathOperation.OperationID.IsEmpty then
begin
ASwagPathOperation.OperationID := AMethod.Name;
end;
// dt [2019-10-31]
ASwagPathOperation.Deprecated := MVCSwagSummaryAttribute(lAttr).Deprecated;
end;
if LAttr is MVCConsumesAttribute then
if lAttr is MVCConsumesAttribute then
begin
ASwagPathOperation.Consumes.Add(MVCConsumesAttribute(LAttr).Value)
ASwagPathOperation.Consumes.Add(MVCConsumesAttribute(lAttr).Value)
end;
if LAttr is MVCProducesAttribute then
if lAttr is MVCProducesAttribute then
begin
ASwagPathOperation.Produces.Add(MVCProducesAttribute(LAttr).Value)
ASwagPathOperation.Produces.Add(MVCProducesAttribute(lAttr).Value)
end;
if LAttr is MVCSwagResponsesAttribute then
if lAttr is MVCSwagResponsesAttribute then
begin
LSwagResponsesAttr := MVCSwagResponsesAttribute(LAttr);
lSwagResponsesAttr := MVCSwagResponsesAttribute(lAttr);
LSwagResponse := TSwagResponse.Create;
LSwagResponse.StatusCode := LSwagResponsesAttr.StatusCode.ToString;
LSwagResponse.Description := LSwagResponsesAttr.Description;
if not LSwagResponsesAttr.JsonSchema.IsEmpty then
LSwagResponse.Schema.JsonSchema := TJSONObject.ParseJSONValue(LSwagResponsesAttr.JsonSchema) as TJSONObject
else if Assigned(LSwagResponsesAttr.JsonSchemaClass) then
LSwagResponse.Schema.JsonSchema := ExtractJsonSchemaFromClass(LSwagResponsesAttr.JsonSchemaClass,
LSwagResponsesAttr.IsArray);
ASwagPathOperation.Responses.Add(LSwagResponse.StatusCode, LSwagResponse);
lSwagResponse := TSwagResponse.Create;
lSwagResponse.StatusCode := lSwagResponsesAttr.StatusCode.ToString;
lSwagResponse.Description := lSwagResponsesAttr.Description;
if not lSwagResponsesAttr.JsonSchema.IsEmpty then
begin
lSwagResponse.Schema.JsonSchema := TJSONObject.ParseJSONValue(lSwagResponsesAttr.JsonSchema) as TJSONObject
end
else if Assigned(lSwagResponsesAttr.JsonSchemaClass) then
begin
lSwagResponse.Schema.JsonSchema := ExtractJsonSchemaFromClass(lSwagResponsesAttr.JsonSchemaClass,
lSwagResponsesAttr.IsArray);
end;
ASwagPathOperation.Responses.Add(lSwagResponse.StatusCode, lSwagResponse);
end;
end;
@ -477,15 +459,15 @@ begin
if ASwagPathOperation.Responses.Count <= 0 then
begin
LSwagResponse := TSwagResponse.Create;
LSwagResponse.StatusCode := HTTP_STATUS.OK.ToString;
LSwagResponse.Description := 'Ok';
ASwagPathOperation.Responses.Add(LSwagResponse.StatusCode, LSwagResponse);
lSwagResponse := TSwagResponse.Create;
lSwagResponse.StatusCode := HTTP_STATUS.OK.ToString;
lSwagResponse.Description := 'Ok';
ASwagPathOperation.Responses.Add(lSwagResponse.StatusCode, lSwagResponse);
LSwagResponse := TSwagResponse.Create;
LSwagResponse.StatusCode := HTTP_STATUS.InternalServerError.ToString;
LSwagResponse.Description := 'Internal server error';
ASwagPathOperation.Responses.Add(LSwagResponse.StatusCode, LSwagResponse);
lSwagResponse := TSwagResponse.Create;
lSwagResponse.StatusCode := HTTP_STATUS.InternalServerError.ToString;
lSwagResponse.Description := 'Internal server error';
ASwagPathOperation.Responses.Add(lSwagResponse.StatusCode, lSwagResponse);
end;
end;
@ -532,11 +514,10 @@ begin
case APropType.TypeKind of
tkClass:
begin
if (APropType.Handle = TypeInfo(TStream)) or
(APropType.Handle = TypeInfo(TMemoryStream)) or
if (APropType.Handle = TypeInfo(TStream)) or (APropType.Handle = TypeInfo(TMemoryStream)) or
(APropType.Handle = TypeInfo(TStringStream)) then
Result := stString
else if RttiTypeIsObjectList(APropType) then
else if TypeIsEnumerable(APropType) then
Result := stArray
else
Result := stObject;
@ -577,71 +558,67 @@ end;
class function TMVCSwagger.GetJWTAuthenticationPath(const AJWTUrlSegment: string): TSwagPath;
var
LSwagPathOp: TSwagPathOperation;
LSwagResponse: TSwagResponse;
lSwagPathOp: TSwagPathOperation;
lSwagResponse: TSwagResponse;
begin
LSwagPathOp := TSwagPathOperation.Create;
LSwagPathOp.Tags.Add('JWT Authentication');
LSwagPathOp.Operation := ohvPost;
LSwagPathOp.Security.Add(SECURITY_BASIC_NAME);
LSwagPathOp.Description := 'Create JSON Web Token';
LSwagPathOp.Produces.Add(TMVCMediaType.APPLICATION_JSON);
lSwagPathOp := TSwagPathOperation.Create;
lSwagPathOp.Tags.Add('JWT Authentication');
lSwagPathOp.Operation := ohvPost;
lSwagPathOp.Security.Add(SECURITY_BASIC_NAME);
lSwagPathOp.Description := 'Create JSON Web Token';
lSwagPathOp.Produces.Add(TMVCMediaType.APPLICATION_JSON);
LSwagResponse := TSwagResponse.Create;
LSwagResponse.StatusCode := HTTP_STATUS.Unauthorized.ToString;
LSwagResponse.Description := 'Invalid authorization type';
LSwagPathOp.Responses.Add(LSwagResponse.StatusCode, LSwagResponse);
lSwagResponse := TSwagResponse.Create;
lSwagResponse.StatusCode := HTTP_STATUS.Unauthorized.ToString;
lSwagResponse.Description := 'Invalid authorization type';
lSwagPathOp.Responses.Add(lSwagResponse.StatusCode, lSwagResponse);
LSwagResponse := TSwagResponse.Create;
LSwagResponse.StatusCode := HTTP_STATUS.Forbidden.ToString;
LSwagResponse.Description := 'Forbidden';
LSwagPathOp.Responses.Add(LSwagResponse.StatusCode, LSwagResponse);
lSwagResponse := TSwagResponse.Create;
lSwagResponse.StatusCode := HTTP_STATUS.Forbidden.ToString;
lSwagResponse.Description := 'Forbidden';
lSwagPathOp.Responses.Add(lSwagResponse.StatusCode, lSwagResponse);
LSwagResponse := TSwagResponse.Create;
LSwagResponse.StatusCode := HTTP_STATUS.InternalServerError.ToString;
LSwagResponse.Description := 'Internal server error';
LSwagPathOp.Responses.Add(LSwagResponse.StatusCode, LSwagResponse);
lSwagResponse := TSwagResponse.Create;
lSwagResponse.StatusCode := HTTP_STATUS.InternalServerError.ToString;
lSwagResponse.Description := 'Internal server error';
lSwagPathOp.Responses.Add(lSwagResponse.StatusCode, lSwagResponse);
LSwagResponse := TSwagResponse.Create;
LSwagResponse.StatusCode := HTTP_STATUS.OK.ToString;
LSwagResponse.Description := 'OK';
LSwagResponse.Schema.JsonSchema := TJSONObject.ParseJSONValue(JWT_JSON_SCHEMA) as TJSONObject;
LSwagPathOp.Responses.Add(LSwagResponse.StatusCode, LSwagResponse);
lSwagResponse := TSwagResponse.Create;
lSwagResponse.StatusCode := HTTP_STATUS.OK.ToString;
lSwagResponse.Description := 'OK';
lSwagResponse.Schema.JsonSchema := TJSONObject.ParseJSONValue(JWT_JSON_SCHEMA) as TJSONObject;
lSwagPathOp.Responses.Add(lSwagResponse.StatusCode, lSwagResponse);
Result := TSwagPath.Create;
Result.Uri := AJwtUrlSegment;
Result.Operations.Add(LSwagPathOp);
Result.Uri := AJWTUrlSegment;
Result.Operations.Add(lSwagPathOp);
end;
class function TMVCSwagger.GetMVCSwagParamsFromMethod(const AMethod: TRttiMethod): TArray<MVCSwagParamAttribute>;
var
LAttr: TCustomAttribute;
lAttr: TCustomAttribute;
begin
SetLength(Result, 0);
for LAttr in AMethod.GetAttributes do
for lAttr in AMethod.GetAttributes do
begin
if LAttr is MVCSwagParamAttribute then
if lAttr is MVCSwagParamAttribute then
begin
Insert([MVCSwagParamAttribute(LAttr)], Result, High(Result));
Insert([MVCSwagParamAttribute(lAttr)], Result, High(Result));
end;
end;
end;
class function TMVCSwagger.GetParamsFromMethod(const AResourcePath: string; const AMethod: TRttiMethod):
TArray<TSwagRequestParameter>;
class function TMVCSwagger.GetParamsFromMethod(const AResourcePath: string; const AMethod: TRttiMethod)
: TArray<TSwagRequestParameter>;
function TryGetMVCPathParamByName(const AParams: TArray<MVCSwagParamAttribute>;
const
AParamName:
string;
out AMVCParam: MVCSwagParamAttribute;
out AIndex: Integer): Boolean;
function TryGetMVCPathParamByName(const AParams: TArray<MVCSwagParamAttribute>; const AParamName: string;
out AMVCParam: MVCSwagParamAttribute; out AIndex: Integer): Boolean;
var
I: Integer;
begin
Result := False;
AMVCParam := nil;
AIndex := - 1;
AIndex := -1;
for I := Low(AParams) to High(AParams) do
if SameText(AParams[I].ParamName, AParamName) and (AParams[I].ParamLocation = plPath) then
begin
@ -652,73 +629,81 @@ class function TMVCSwagger.GetParamsFromMethod(const AResourcePath: string; cons
end;
var
LMatches: TMatchCollection;
LMatch: TMatch;
LParamName: string;
LMethodParam: TRttiParameter;
LSwagParam: TSwagRequestParameter;
LMVCSwagParams: TArray<MVCSwagParamAttribute>;
LMVCParam: MVCSwagParamAttribute;
LIndex: Integer;
lMatches: TMatchCollection;
lMatch: TMatch;
lParamName: string;
lMethodParam: TRttiParameter;
lSwagParam: TSwagRequestParameter;
lMVCSwagParams: TArray<MVCSwagParamAttribute>;
lMVCParam: MVCSwagParamAttribute;
lIndex: Integer;
I: Integer;
begin
LMVCSwagParams := GetMVCSwagParamsFromMethod(AMethod);
lMVCSwagParams := GetMVCSwagParamsFromMethod(AMethod);
SetLength(Result, 0);
// Path parameters
LMatches := TRegEx.Matches(AResourcePath, '({)([\w_]+)(})', [roIgnoreCase, roMultiLine]);
for LMatch in LMatches do
lMatches := TRegEx.Matches(AResourcePath, '({)([\w_]+)(})', [roIgnoreCase, roMultiLine]);
for lMatch in lMatches do
begin
LParamName := LMatch.Groups[2].Value;
for LMethodParam in AMethod.GetParameters do
lParamName := lMatch.Groups[2].Value;
for lMethodParam in AMethod.GetParameters do
begin
if SameText(LMethodParam.Name, LParamName) then
if SameText(lMethodParam.Name, lParamName) then
begin
LSwagParam := TSwagRequestParameter.Create;
lSwagParam := TSwagRequestParameter.Create;
if TryGetMVCPathParamByName(LMVCSwagParams, LParamName, LMVCParam, LIndex) then
if TryGetMVCPathParamByName(lMVCSwagParams, lParamName, lMVCParam, lIndex) then
begin
LSwagParam.Name := LParamName;
LSwagParam.InLocation := MVCParamLocationToSwagRequestParamInLocation(LMVCParam.ParamLocation);
LSwagParam.Required := LMVCParam.Required;
LSwagParam.TypeParameter := MVCParamTypeToSwagTypeParameter(LMVCParam.ParamType);
LSwagParam.Description := LMVCParam.ParamDescription;
if not LMVCParam.JsonSchema.IsEmpty then
LSwagParam.Schema.JsonSchema := TJSONObject.ParseJSONValue(LMVCParam.JsonSchema) as TJSONObject
else if Assigned(LMVCParam.JsonSchemaClass) then
LSwagParam.Schema.JsonSchema := ExtractJsonSchemaFromClass(LMVCParam.JsonSchemaClass,
LMVCParam.ParamType = ptArray);
Delete(LMVCSwagParams, LIndex, 1);
lSwagParam.Name := lParamName;
lSwagParam.InLocation := MVCParamLocationToSwagRequestParamInLocation(lMVCParam.ParamLocation);
lSwagParam.Required := lMVCParam.Required;
lSwagParam.TypeParameter := MVCParamTypeToSwagTypeParameter(lMVCParam.ParamType);
lSwagParam.Description := lMVCParam.ParamDescription;
if not lMVCParam.JsonSchema.IsEmpty then
begin
lSwagParam.Schema.JsonSchema := TJSONObject.ParseJSONValue(lMVCParam.JsonSchema) as TJSONObject
end
else if Assigned(lMVCParam.JsonSchemaClass) then
begin
lSwagParam.Schema.JsonSchema := ExtractJsonSchemaFromClass(lMVCParam.JsonSchemaClass,
lMVCParam.ParamType = ptArray);
end;
Delete(lMVCSwagParams, lIndex, 1);
end
else
begin
LSwagParam.Name := LParamName;
LSwagParam.InLocation := rpiPath;
LSwagParam.Required := True;
LSwagParam.TypeParameter := RttiTypeToSwagType(LMethodParam.ParamType);
lSwagParam.Name := lParamName;
lSwagParam.InLocation := rpiPath;
lSwagParam.Required := True;
lSwagParam.TypeParameter := RttiTypeToSwagType(lMethodParam.ParamType);
end;
Insert([LSwagParam], Result, High(Result));
Insert([lSwagParam], Result, High(Result));
end;
end;
end;
// Other parameters
for I := Low(LMVCSwagParams) to High(LMVCSwagParams) do
for I := Low(lMVCSwagParams) to High(lMVCSwagParams) do
begin
LSwagParam := TSwagRequestParameter.Create;
LSwagParam.Name := LMVCSwagParams[I].ParamName;
LSwagParam.InLocation := MVCParamLocationToSwagRequestParamInLocation(LMVCSwagParams[I].ParamLocation);
LSwagParam.Required := LMVCSwagParams[I].Required;
LSwagParam.TypeParameter := MVCParamTypeToSwagTypeParameter(LMVCSwagParams[I].ParamType);
LSwagParam.Description := LMVCSwagParams[I].ParamDescription;
if not LMVCSwagParams[I].JsonSchema.IsEmpty then
LSwagParam.Schema.JsonSchema := TJSONObject.ParseJSONValue(LMVCSwagParams[I].JsonSchema) as TJSONObject
else if Assigned(LMVCSwagParams[I].JsonSchemaClass) then
LSwagParam.Schema.JsonSchema := ExtractJsonSchemaFromClass(LMVCSwagParams[I].JsonSchemaClass,
LMVCSwagParams[I].ParamType = ptArray);
lSwagParam := TSwagRequestParameter.Create;
lSwagParam.Name := lMVCSwagParams[I].ParamName;
lSwagParam.InLocation := MVCParamLocationToSwagRequestParamInLocation(lMVCSwagParams[I].ParamLocation);
lSwagParam.Required := lMVCSwagParams[I].Required;
lSwagParam.TypeParameter := MVCParamTypeToSwagTypeParameter(lMVCSwagParams[I].ParamType);
lSwagParam.Description := lMVCSwagParams[I].ParamDescription;
if not lMVCSwagParams[I].JsonSchema.IsEmpty then
begin
lSwagParam.Schema.JsonSchema := TJSONObject.ParseJSONValue(lMVCSwagParams[I].JsonSchema) as TJSONObject
end
else if Assigned(lMVCSwagParams[I].JsonSchemaClass) then
begin
lSwagParam.Schema.JsonSchema := ExtractJsonSchemaFromClass(lMVCSwagParams[I].JsonSchemaClass,
lMVCSwagParams[I].ParamType = ptArray);
end;
Insert([LSwagParam], Result, High(Result));
Insert([lSwagParam], Result, High(Result));
end;
end;
@ -726,20 +711,20 @@ end;
class function TMVCSwagger.MethodRequiresAuthentication(const AMethod: TRttiMethod; const AType: TRttiType;
out AAuthenticationTypeName: string): Boolean;
var
LAttr: TCustomAttribute;
lAttr: TCustomAttribute;
begin
Result := False;
AAuthenticationTypeName := '';
for LAttr in AMethod.GetAttributes do
if LAttr is MVCRequiresAuthenticationAttribute then
for lAttr in AMethod.GetAttributes do
if lAttr is MVCRequiresAuthenticationAttribute then
begin
AAuthenticationTypeName := SECURITY_BEARER_NAME;
Exit(True);
end
else if LAttr is MVCSwagAuthenticationAttribute then
else if lAttr is MVCSwagAuthenticationAttribute then
begin
case MVCSwagAuthenticationAttribute(LAttr).AuthenticationType of
case MVCSwagAuthenticationAttribute(lAttr).AuthenticationType of
atBasic:
AAuthenticationTypeName := SECURITY_BASIC_NAME;
atJsonWebToken:
@ -748,15 +733,15 @@ begin
Exit(True);
end;
for LAttr in AType.GetAttributes do
if LAttr is MVCRequiresAuthenticationAttribute then
for lAttr in AType.GetAttributes do
if lAttr is MVCRequiresAuthenticationAttribute then
begin
AAuthenticationTypeName := SECURITY_BEARER_NAME;
Exit(True);
end
else if LAttr is MVCSwagAuthenticationAttribute then
else if lAttr is MVCSwagAuthenticationAttribute then
begin
case MVCSwagAuthenticationAttribute(LAttr).AuthenticationType of
case MVCSwagAuthenticationAttribute(lAttr).AuthenticationType of
atBasic:
AAuthenticationTypeName := SECURITY_BASIC_NAME;
atJsonWebToken:
@ -766,8 +751,8 @@ begin
end;
end;
class function TMVCSwagger.MVCHttpMethodToSwagPathOperation(const AMVCHTTPMethod: TMVCHTTPMethodType):
TSwagPathTypeOperation;
class function TMVCSwagger.MVCHttpMethodToSwagPathOperation(const AMVCHTTPMethod: TMVCHTTPMethodType)
: TSwagPathTypeOperation;
begin
case AMVCHTTPMethod of
httpGET:
@ -790,8 +775,7 @@ begin
end;
class function TMVCSwagger.MVCParamLocationToSwagRequestParamInLocation(const AMVCSwagParamLocation
: TMVCSwagParamLocation)
: TSwagRequestParameterInLocation;
: TMVCSwagParamLocation): TSwagRequestParameterInLocation;
begin
case AMVCSwagParamLocation of
plQuery:
@ -837,18 +821,18 @@ end;
{ MVCSwagSummary }
constructor MVCSwagSummaryAttribute.Create(const ATags, ADescription: string; const APathId: string;
constructor MVCSwagSummaryAttribute.Create(const ATags, ADescription: string; const AOperationId: string;
ADeprecated: Boolean);
begin
FTags := ATags;
FDescription := ADescription;
FOperationID := APathId;
FDeprecated := ADeprecated;
fTags := ATags;
fDescription := ADescription;
fOperationID := AOperationId;
fDeprecated := ADeprecated;
end;
function MVCSwagSummaryAttribute.GetTags: TArray<string>;
begin
Result := FTags.Split([',']);
Result := fTags.Split([',']);
end;
{ MVCSwagResponsesAttribute }
@ -856,57 +840,58 @@ end;
constructor MVCSwagResponsesAttribute.Create(const AStatusCode: Integer; const ADescription: string;
const AJsonSchema: string);
begin
FStatusCode := AStatusCode;
FDescription := ADescription;
FJsonSchema := AJsonSchema;
FJsonSchemaClass := nil;
fStatusCode := AStatusCode;
fDescription := ADescription;
fJsonSchema := AJsonSchema;
fJsonSchemaClass := nil;
end;
constructor MVCSwagResponsesAttribute.Create(const AStatusCode: Integer; const ADescription: string;
const AJsonSchemaClass: TClass; const AIsArray: Boolean);
begin
Create(AStatusCode, ADescription, '');
FJsonSchemaClass := AJsonSchemaClass;
FIsArray := AIsArray;
fJsonSchemaClass := AJsonSchemaClass;
fIsArray := AIsArray;
end;
{ MVCSwagParamAttribute }
constructor MVCSwagParamAttribute.Create(const AParamLocation: TMVCSwagParamLocation; const AParamName,
AParamDescription: string; const AParamType: TMVCSwagParamType; const ARequired: Boolean; const AJsonSchema: string);
constructor MVCSwagParamAttribute.Create(const AParamLocation: TMVCSwagParamLocation;
const AParamName, AParamDescription: string; const AParamType: TMVCSwagParamType; const ARequired: Boolean;
const AJsonSchema: string);
begin
FParamLocation := AParamLocation;
FParamName := AParamName;
FParamDescription := AParamDescription;
FParamType := AParamType;
FRequired := ARequired;
FJsonSchema := AJsonSchema;
FJsonSchemaClass := nil;
fParamLocation := AParamLocation;
fParamName := AParamName;
fParamDescription := AParamDescription;
fParamType := AParamType;
fRequired := ARequired;
fJsonSchema := AJsonSchema;
fJsonSchemaClass := nil;
end;
constructor MVCSwagParamAttribute.Create(const AParamLocation: TMVCSwagParamLocation; const AParamName,
AParamDescription: string; const AJsonSchemaClass: TClass; const AParamType: TMVCSwagParamType;
constructor MVCSwagParamAttribute.Create(const AParamLocation: TMVCSwagParamLocation;
const AParamName, AParamDescription: string; const AJsonSchemaClass: TClass; const AParamType: TMVCSwagParamType;
const ARequired: Boolean);
begin
Create(AParamLocation, AParamName, AParamDescription, AParamType, ARequired, '');
FJsonSchemaClass := AJsonSchemaClass;
fJsonSchemaClass := AJsonSchemaClass;
end;
{ MVCSwagJsonSchemaFieldAttribute }
{ MVCSwagJSONSchemaFieldAttribute }
constructor MVCSwagJsonSchemaFieldAttribute.Create(const AFieldName, ADescription: string; const ARequired,
ANullable: Boolean);
constructor MVCSwagJSONSchemaFieldAttribute.Create(const AFieldName, ADescription: string;
const ARequired, ANullable: Boolean);
begin
Create(stUnknown, AFieldName, ADescription, ARequired, ANullable);
end;
constructor MVCSwagJsonSchemaFieldAttribute.Create(const ASchemaFieldType: TMVCSwagSchemaType; const AFieldName,
ADescription: string; const ARequired, ANullable: Boolean);
constructor MVCSwagJSONSchemaFieldAttribute.Create(const ASchemaFieldType: TMVCSwagSchemaType;
const AFieldName, ADescription: string; const ARequired, ANullable: Boolean);
begin
FSchemaFieldType := ASchemaFieldType;
FFieldName := AFieldName;
FDescription := ADescription;
FRequired := ARequired;
fDescription := ADescription;
fRequired := ARequired;
FNullable := ANullable;
end;

View File

@ -1,2 +1,2 @@
const
DMVCFRAMEWORK_VERSION = '3.2.0 (boron) RC1';
DMVCFRAMEWORK_VERSION = '3.2.0 (boron) RC2';

View File

@ -53,7 +53,7 @@ type
FName: string;
// The MVCListOfAttribute attribute can be placed in Field or Property
[MVCListOf(TNote)]
//[MVCListOf(TNote)]
FNotes: TObjectList<TNote>;
public
constructor Create;
@ -63,7 +63,7 @@ type
[MVCNameAs('Name')]
property name: string read FName write FName;
[MVCListOf(TNote)]
// [MVCListOf(TNote)]
property Notes: TObjectList<TNote> read FNotes write FNotes;
end;
@ -82,10 +82,10 @@ type
FDepartment: TDepartment;
FDepartmentNull: TDepartment;
[MVCListOf(TNote)]
// [MVCListOf(TNote)]
FNotes: TObjectList<TNote>;
[MVCListOf(TNote)]
// [MVCListOf(TNote)]
FNotesEmpty: TObjectList<TNote>;
// The MVCValueAsTypeAttribute attribute can be placed in Field or Property
@ -117,10 +117,10 @@ type
property Department: TDepartment read FDepartment write FDepartment;
property DepartmentNull: TDepartment read FDepartmentNull write FDepartmentNull;
[MVCListOf(TNote)]
//[MVCListOf(TNote)]
property Notes: TObjectList<TNote> read FNotes;
[MVCListOf(TNote)]
//[MVCListOf(TNote)]
property NotesEmpty: TObjectList<TNote> read FNotesEmpty;
[MVCValueAsType(System.TypeInfo(string))]