DelphiMVCFramework 3.2.0-boron-RC7
Pre-releaseDelphiMVCFramework 3.2.0-boron-RC7
-
New! Added Nullable support in MVCActiveRecord (nullables defined in
MVCFramework.Nullables.pas
)! Check activerecord_showcase sample. -
New! Added non autogenerated primary keys in MVCActiveRecord! Check activerecord_showcase sample.
-
New! Complete support for nullable types in the default serializer (nullables defined in
MVCFramework.Nullables.pas
) -
New! Added
ncCamelCase
andncPascalCase
to the available attribute formatters.MVCNameCase Property/Field Name Rendered Name ncUpperCase Cod_Article COD_ARTICLE ncLowerCase Cod_Article cod_article ncPascalCase Cod_Article CodArticle ncPascalCase CodArticle CodArticle ncPascalCase _WITH__UNDERSCORES_
WithUnderscores ncCamelCase Cod_Article codArticle ncCamelCase CodArticle codArticle ncCamelCase _WITH__UNDERSCORES_
WithUnderscores -
New! Added Swagger support (thanks to João Antônio Duarte and Geoffrey Smith)
-
New! Attribute
MVCDoNotDeserialize
. If marked with this RTTI attribute, a property or a field is not deserialized and its value remain the same as was before the object deserialization. -
New! ObjectDict function is the suggested way to render all the most common data types. It returns a
IMVCObjectDictionary
which is automatically rendered by the renders. Check therenders.dproj
sample. Here's some example of the shining newObjectDict()
Example 1: Rendering a list of objects not freeing them after rendering
Classic
procedure TRenderSampleController.GetLotOfPeople;
begin
Render<TPerson>(GetPeopleList, False);
end;
New approach with ObjectDict
procedure TRenderSampleController.GetLotOfPeople;
begin
Render(ObjectDict(False).Add('data', GetPeopleList));
end;
Example 2: Rendering a list of objects and automatically free them after rendering
Classic
procedure TRenderSampleController.GetLotOfPeople;
begin
Render<TPerson>(GetPeopleList);
end;
New approach with ObjectDict
procedure TRenderSampleController.GetLotOfPeople;
begin
Render(ObjectDict().Add('data', GetPeopleList));
end;
Example 3: Rendering a list of objects adding links for HATEOAS support
Classic
procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS;
var
p: TPerson;
People: TObjectList<TPerson>;
begin
People := TObjectList<TPerson>.Create(True);
{$REGION 'Fake data'}
p := TPerson.Create;
p.FirstName := 'Daniele';
p.LastName := 'Teti';
p.DOB := EncodeDate(1979, 8, 4);
p.Married := True;
People.Add(p);
p := TPerson.Create;
p.FirstName := 'John';
p.LastName := 'Doe';
p.DOB := EncodeDate(1879, 10, 2);
p.Married := False;
People.Add(p);
p := TPerson.Create;
p.FirstName := 'Jane';
p.LastName := 'Doe';
p.DOB := EncodeDate(1883, 1, 5);
p.Married := True;
People.Add(p);
{$ENDREGION}
Render<TPerson>(People, True,
procedure(const APerson: TPerson; const Links: IMVCLinks)
begin
Links
.AddRefLink
.Add(HATEOAS.HREF, '/people/' + APerson.ID.ToString)
.Add(HATEOAS.REL, 'self')
.Add(HATEOAS._TYPE, 'application/json')
.Add('title', 'Details for ' + APerson.FullName);
Links
.AddRefLink
.Add(HATEOAS.HREF, '/people')
.Add(HATEOAS.REL, 'people')
.Add(HATEOAS._TYPE, 'application/json');
end);
end;
New approach with ObjectDict
procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS;
var
p: TPerson;
People: TObjectList<TPerson>;
begin
People := TObjectList<TPerson>.Create(True);
{$REGION 'Fake data'}
p := TPerson.Create;
p.FirstName := 'Daniele';
p.LastName := 'Teti';
p.DOB := EncodeDate(1979, 8, 4);
p.Married := True;
People.Add(p);
p := TPerson.Create;
p.FirstName := 'John';
p.LastName := 'Doe';
p.DOB := EncodeDate(1879, 10, 2);
p.Married := False;
People.Add(p);
p := TPerson.Create;
p.FirstName := 'Jane';
p.LastName := 'Doe';
p.DOB := EncodeDate(1883, 1, 5);
p.Married := True;
People.Add(p);
{$ENDREGION}
Render(ObjectDict().Add('data', People,
procedure(const APerson: TObject; const Links: IMVCLinks)
begin
Links
.AddRefLink
.Add(HATEOAS.HREF, '/people/' + TPerson(APerson).ID.ToString)
.Add(HATEOAS.REL, 'self')
.Add(HATEOAS._TYPE, 'application/json')
.Add('title', 'Details for ' + TPerson(APerson).FullName);
Links
.AddRefLink
.Add(HATEOAS.HREF, '/people')
.Add(HATEOAS.REL, 'people')
.Add(HATEOAS._TYPE, 'application/json');
end));
end;
ObjectDict
is able to render multiple data sources (datasets, objectlists, objects or StrDict) at the same time using different casing, HATEOAS callbacks and modes.
procedure TTestServerController.TestObjectDict;
var
lDict: IMVCObjectDictionary;
begin
lDict := ObjectDict(false)
.Add('ncUpperCase_List', GetDataSet, nil, dstAllRecords, ncUpperCase)
.Add('ncLowerCase_List', GetDataSet, nil, dstAllRecords, ncLowerCase)
.Add('ncCamelCase_List', GetDataSet, nil, dstAllRecords, ncCamelCase)
.Add('ncPascalCase_List', GetDataSet, nil, dstAllRecords, ncPascalCase)
.Add('ncUpperCase_Single', GetDataSet, nil, dstSingleRecord, ncUpperCase)
.Add('ncLowerCase_Single', GetDataSet, nil, dstSingleRecord, ncLowerCase)
.Add('ncCamelCase_Single', GetDataSet, nil, dstSingleRecord, ncCamelCase)
.Add('ncPascalCase_Single', GetDataSet, nil, dstSingleRecord, ncPascalCase)
.Add('meta', StrDict(['page'], ['1']));
Render(lDict);
end;
ObjectDict is the suggested way to renders data. However, the other ones are still there and works as usual.
- New! Added SQLGenerator and RQL compiler for PostgreSQL, SQLite and MSSQLServer (in addition to MySQL, MariaDB, Firebird and Interbase)
- New! MVCNameAs attribute got the param
Fixed
(default: false). IfFixed
is true, then the name is not processed by theMVCNameCase
attribute assigned to the owner type. - New! Added support for interfaces serialization - now it is possible to serialize Spring4D collections (thanks to João Antônio Duarte)
- New! Added support for Spring4D Nullable Types - check (thanks to João Antônio Duarte)
- New! Added
OnRouterLog
event to log custom information for each request (thanks to Andrea Ciotti for the first implementation and its PR) - New! Optionally load system controllers (those who provide
/describeserver.info
,/describeplatform.info
and/serverconfig.info
system actions) settingConfig[TMVCConfigKey.LoadSystemControllers] := 'false';
in the configuration block. - Improved! Now the router consider
Accept:*/*
compatible for everyMVCProduces
values - Improved! Greatly improved support for HATEOAS in renders. Check
TRenderSampleController.GetPeople_AsObjectList_HATEOS
and all the others actions end withHATEOS
inrenders.dproj
sample)
//Now is really easy to add "links" property automatically for each collection element while rendering
Render<TPerson>(People, True,
procedure(const APerson: TPerson; const Links: IMVCLinks)
begin
Links.AddRefLink
.Add(HATEOAS.HREF, '/people/' + APerson.ID.ToString)
.Add(HATEOAS.REL, 'self')
.Add(HATEOAS._TYPE, 'application/json')
.Add('title', 'Details for ' + APerson.FullName);
Links.AddRefLink
.Add(HATEOAS.HREF, '/people')
.Add(HATEOAS.REL, 'people')
.Add(HATEOAS._TYPE, 'application/json');
end);
//Datasets have a similar anon method to do the same thing
Render(lDM.qryCustomers, False,
procedure(const DS: TDataset; const Links: IMVCLinks)
begin
Links.AddRefLink
.Add(HATEOAS.HREF, '/customers/' + DS.FieldByName('cust_no').AsString)
.Add(HATEOAS.REL, 'self')
.Add(HATEOAS._TYPE, 'application/json');
Links.AddRefLink
.Add(HATEOAS.HREF, '/customers/' + DS.FieldByName('cust_no').AsString + '/orders')
.Add(HATEOAS.REL, 'orders')
.Add(HATEOAS._TYPE, 'application/json');
end);
//Single object rendering allows HATEOAS too!
Render(lPerson, False,
procedure(const AObject: TObject; const Links: IMVCLinks)
begin
Links.AddRefLink
.Add(HATEOAS.HREF, '/people/' + TPerson(AObject).ID.ToString)
.Add(HATEOAS.REL, 'self')
.Add(HATEOAS._TYPE, TMVCMediaType.APPLICATION_JSON);
Links.AddRefLink
.Add(HATEOAS.HREF, '/people')
.Add(HATEOAS.REL, 'people')
.Add(HATEOAS._TYPE, TMVCMediaType.APPLICATION_JSON);
end);
- Better packages organization (check
packages
folder) - New!
TMVCActiveRecord.Count
method (e.g.TMVCActiveRecord.Count(TCustomer)
returns the number of records for the entity mapped by the classTCustomer
) - Change!
TMVCACtiveRecord.GetByPK<T>
raises an exception by default if the record is not found - optionally can returnsnil
using new parameterRaiseExceptionIfNotFound
- New!
contains
clause has been added in the RQL compiler for Firebird and Interbase - New!
TMVCAnalyticsMiddleware
to do automatic analytics on the API (generates a CSV file). Based on an idea by Nirav Kaku (https://www.facebook.com/nirav.kaku). Check the sample in\samples\middleware_analytics\
- New!
TMVCActiveRecord.DeleteAll
deletes all the records from a table - New!
TMVCActiveRecord.DeleteRQL
deletes records using anRQL
expression aswhere
clause. - New!
TMVCActiveRecord.Store
which automatically executes Insert or Update considering primary key value. - New!
TMVCActiveRecord
allows to use table name and field name with spaces (currently supported only by the PostgreSQL compiler). - New! Microsoft SQLServer Support in
MVCActiveRecord
and RQL (thanks to one of the biggest Delphi based company in Italy which heavily uses DMVCFramework and DMSContainer) - New! SQLite support in
MVCActiveRecord
and RQL, so thatMVCActiveRecord
can be used also for Delphi mobile projects! - Default JSON Serializer can verbatim pass properties with type
JsonDataObjects.TJSONObject
without usingstring
as carrier of JSON - Improved!
ActiveRecordShowCase
sample is much better now. - Improved! All
ActiveRecord
methods which retrieve records can now specify the data type of each parameter (using Delphi'sTFieldType
enumeration). - Improved! In case of unhandled exception
TMVCEngine
is compliant with the default response content-type (usually it did would reply usingtext/plain
). - Added! New overloads for all the Log* calls. Now it is possible to call
LogD(lMyObject)
to get loggedlMyObject
as JSON (custom type serializers not supported in log). - New!
StrDict(array of string, array of string)
function allows to render a dictionary of strings in a really simple way. See the following action sample.
procedure TMy.GetPeople(const Value: Integer);
begin
if Value mod 2 <> 0 then
begin
raise EMVCException.Create(HTTP_STATUS.NotAcceptable, 'We don''t like odd numbers');
end;
Render(
StrDict(
['id', 'message'],
['123', 'We like even numbers, thank you for your ' + Value.ToString]
));
end;
-
New! Custom Exception Handling (Based on work of David Moorhouse). Sample "custom_exception_handling" show how to use it.
-
Improved! Exceptions rendering while using MIME types different to
application/json
. -
SSL Server support for
TMVCListener
(Thanks to Sven Harazim) -
Improved! Datasets serialization speed improvement. In some case the performance improves of 2 order of magnitude. (Thanks to https://github.com/pedrooliveira01)
-
New! Added
in
operator in RQL parser (Thank you to João Antônio Duarte for his initial work on this) -
New! Added
TMVCActiveRecord.Count<T>(RQL)
to count record based on RQL criteria -
New!
TMVCActiveRecord
can handle non autogenerated primary key. -
New! Experimental (alpha stage) support for Android servers!
-
New! Added support for
X-HTTP-Method-Override
to work behind corporate firewalls. -
New Sample! Server in DLL
-
Added new method in the dataset helper to load data into a dataset from a specific JSONArray property of a JSONObject
procedure TDataSetHelper.LoadJSONArrayFromJSONObjectProperty(const AJSONObjectString: string; const aPropertyName: String);
-
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>
,TArray<Integer>
andTArray<Double>
in default JSON serializer (Thank you Pedro Oliveira) -
Improved JWT Standard Compliance! Thanks to Vinicius Sanchez for his work on issue #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. -
New! Serialization callback for custom
TDataSet
descendants serialization inTMVCJsonDataObjectsSerializer
.
procedure TMainForm.btnDataSetToJSONArrayClick(Sender: TObject);
var
lSer: TMVCJsonDataObjectsSerializer;
lJArray: TJSONArray;
begin
FDQuery1.Open();
lSer := TMVCJsonDataObjectsSerializer.Create;
try
lJArray := TJSONArray.Create;
try
lSer.DataSetToJsonArray(FDQuery1, lJArray, TMVCNameCase.ncLowerCase, [],
procedure(const aField: TField; const aJsonObject: TJSONObject; var Handled: Boolean)
begin
if SameText(aField.FieldName, 'created_at') then
begin
aJsonObject.S['year_and_month'] := FormatDateTime('yyyy-mm', TDateTimeField(aField).Value);
Handled := True;
end;
end);
//The json objects will not contains "created_at" anymore, but only "year_and_month".
Memo1.Lines.Text := lJArray.ToJSON(false);
finally
lJArray.Free;
end;
finally
lSer.Free;
end;
end;
-
New! Shortcut render' methods which simplify RESTful API development
procedure Render201Created(const Location: String = ''; const Reason: String = 'Created'); virtual;
procedure Render202Accepted(const HREF: String; const ID: String; const Reason: String = 'Accepted'); virtual;
procedure Render204NoContent(const Reason: String = 'No Content'); virtual;
-
Added de/serializing iterables (e.g. generic lists) support without
MVCListOf
attribute (Thank you to João Antônio Duarte).It is now possible to deserialize a generic class like this:
TGenericEntity<T: class> = class private FCode: Integer; FItems: TObjectList<T>; FDescription: string; public constructor Create; destructor Destroy; override; property Code: Integer read FCode write FCode; property Description: string read FDescription write FDescription; // MVCListOf(T) <- No need property Items: TObjectList<T> read FItems write FItems; end;
Before it was not possible because you should add the
MVCListOf
attribute to theTObjectList
type property. -
New! Added serialization support for (thanks to dockerandy for his initial work)
TArray<String>
TArray<Integer>
TArray<Int64>
TArray<Double>
-
New! The MVCAREntitiesGenerator can optionally register all the generated entities also in the
ActiveRecordMappingRegistry
(Thanks to Fabrizio Bitti from bit Time Software) -
New! Children objects lifecycle management in
TMVCActiveRecord
(methodsAddChildren
andRemoveChildren
). Really useful to manage child objects such relations or derived properties and are safe in case of multiple addition of the same object as children.//Having the following declaration type [MVCNameCase(ncCamelCase)] [MVCTable('authors')] TAuthor = class(TPersonEntityBase) private fBooks: TEnumerable<TBookRef>; [MVCTableField('full_name')] fFullName: string; function GetBooks: TEnumerable<TBookRef>; public [MVCNameAs('full_name')] property FullName: string read fFullName write fFullName; property Books: TEnumerable<TBookRef> read GetBooks; end; //method GetBooks can be implemented as follows: implementation function TAuthor.GetBooks: TEnumerable<TBookRef>; begin if fBooks = nil then begin fBooks := TMVCActiveRecord.Where<TBookRef>('author_id = ?', [ID]); AddChildren(fBooks); //fBooks will be freed when self will be freed end; Result := fBooks; end;
-
JSON-RPC Improvements
-
New! Added
TMVCJSONRPCExecutor.ConfigHTTPClient
to fully customize the innerTHTTPClient
(e.g.ConnectionTimeout
,ResponseTimeout
and so on) -
Improved! JSONRPC Automatic Object Publishing can not invoke inherited methods if not explicitly defined with
MVCInheritable
attribute. -
New! Calling
<jsonrpcendpoint>/describe
returns the methods list available for that endpoint. -
New! Full support for named parameters in JSON-RPC call (server and client)
-
Positional parameters example
procedure TMainForm.btnSubtractClick(Sender: TObject); var lReq: IJSONRPCRequest; lResp: IJSONRPCResponse; begin lReq := TJSONRPCRequest.Create; lReq.Method := 'subtract'; lReq.RequestID := Random(1000); lReq.Params.Add(StrToInt(edtValue1.Text)); lReq.Params.Add(StrToInt(edtValue2.Text)); lResp := FExecutor.ExecuteRequest(lReq); edtResult.Text := lResp.Result.AsInteger.ToString; end;
-
Named parameters example
procedure TMainForm.btnSubtractWithNamedParamsClick(Sender: TObject); var lReq: IJSONRPCRequest; lResp: IJSONRPCResponse; begin lReq := TJSONRPCRequest.Create; lReq.Method := 'subtract'; lReq.RequestID := Random(1000); lReq.Params.AddByName('Value1', StrToInt(Edit1.Text)); lReq.Params.AddByName('Value2', StrToInt(Edit2.Text)); lResp := FExecutor.ExecuteRequest(lReq); Edit3.Text := lResp.Result.AsInteger.ToString; end;
-
Check official JSONRPC 2.0 documentation for more examples.
-
-
New! JSONRPC Hooks for published objects
//Called before as soon as the HTTP arrives procedure TMyPublishedObject.OnBeforeRouting(const JSON: TJDOJsonObject); //Called before the invoked method procedure TMyPublishedObject.OnBeforeCall(const JSONRequest: TJDOJsonObject); //Called just before to send response to the client procedure TMyPublishedObject.OnBeforeSendResponse(const JSONResponse: TJDOJsonObject);
-
-
Breaking Change! In
MVCActiveRecord
attributeMVCPrimaryKey
has been removed and merged withMVCTableField
, so nowTMVCActiveRecordFieldOption
is a set offoPrimaryKey
,foAutoGenerated
,foTransient
(checkactiverecord_showcase.dproj
sample). -
Breaking Change! Middleware
OnAfterControllerAction
are now invoked in the same order ofOnBeforeControllerAction
(previously were invoked in reversed order). -
Breaking Change!
TMVCEngine
is no more responsible for static file serving. If you need static files used the newTMVCStaticFilesMiddleware
(check the sample). As conseguenceTMVCConfigKey.DocumentRoot
,TMVCConfigKey.IndexDocument
andTMVCConfigKey.FallbackResource
are no more availables. -
Deprecated!
TDataSetHolder
is deprecated! Use the shining newObjectDict(boolean)
instead. -
Added ability to serialize/deserialize types enumerated by an array of mapped values (Thanks to João Antônio Duarte)
type TMonthEnum = (meJanuary, meFebruary, meMarch, meApril); TEntityWithEnums = class private FMonthMappedNames: TMonthEnum; FMonthEnumName: TMonthEnum; FMonthOrder: TMonthEnum; public // List items separated by comma or semicolon [MVCEnumSerializationType(estEnumMappedValues, 'January,February,March,April')] property MonthMappedNames: TMonthEnum read FMonthMappedNames write FMonthMappedNames; [MVCEnumSerializationType(estEnumName)] property MonthEnumName: TMonthEnum read FMonthEnumName write FMonthEnumName; [MVCEnumSerializationType(estEnumOrd)] property MonthOrder: TMonthEnum read FMonthOrder write FMonthOrder; end; ...
-
New Installation procedure!
- Open the project group (select the correct one from the following table)
- Build all
- Install the design-time package (
dmvcframeworkDT
) - Add the following paths in the Delphi Library Path (here,
C:\DEV\dmvcframework
is thedmvcframework
main folder)C:\DEV\dmvcframework\sources
C:\DEV\dmvcframework\lib\loggerpro
C:\DEV\dmvcframework\lib\swagdoc\Source
C:\DEV\dmvcframework\lib\dmustache
Delphi Version | Project Group |
---|---|
Delphi 10.3 Rio | packages\d103\dmvcframework_group.groupproj |
Delphi 10.2 Tokyo | packages\d102\dmvcframework_group.groupproj |
Delphi 10.1 Berlin | packages\d101\dmvcframework_group.groupproj |
Delphi 10.0 Seattle | packages\d100\dmvcframework_group.groupproj |
Bug Fixes in 3.2.0-boron
- Fixed! issue38
- Fixed! issue184
- Fixed! issue278
- Fixed! issue164
- Fixed! issue182
- Fixed! issue232 (Thanks to João Antônio Duarte)
- Fixed! issue239
- Fixed! issue289 (Thanks to João Antônio Duarte)
- Fixed! issue291 (Thanks to João Antônio Duarte)
- Fixed! issue305 (Thanks to João Antônio Duarte)
- Fixed! issue312
- Fixed! issue330
- Fixed! issue333
- Fixed! issue334
- Fixed! issue336
- Fixed! issue337
- Fixed! issue338
- Fixed! issue239
- Fixed! issue345
- Fixed! issue349
- Fixed! issue350
- Fixed! issue355
- Fixed! issue356
- Fixed! issue362
- Fixed! issue363
- Fixed! issue364 (Thanks to João Antônio Duarte)
- Fixed! issue366
- Fixed! issue376 (Thanks to João Antônio Duarte)
- Fixed! Has been patched a serious security bug affecting deployment configurations which uses internal WebServer to serve static files (do not affect all Apache, IIS or proxied deployments). Thanks to Stephan Munz to have discovered it. Update to dmvcframework-3.2-RC5+ is required for all such kind of deployments.