Releases: danieleteti/delphimvcframework
DelphiMVCFramework 3.4.2-magnesium RC2
Detailed info here
What's Changed in this RC2
Full Changelog: v3.4.2-magnesium-rc1...v3.4.2-magnesium-rc2
Detailed info here
DelphiMVCFramework 3.4.2-magnesium RC1
What's Changed - 👉 Full detailed info here 👈
THIS IS A PRE RELEASE!!
- build(deps): bump axios from 0.21.4 to 1.6.0 in /samples/react/WebApp by @dependabot in #710
- build(deps): bump follow-redirects from 1.15.2 to 1.15.4 in /samples/react/WebApp by @dependabot in #725
- Added DeleteCacheKey method to TMVCCacheController by @gmurt in #731
- Wrong http error code when authenticating by @zaniniflz in #734
- Fixed issue with DeleteCacheKey method by @gmurt in #733
- build(deps): bump follow-redirects from 1.15.4 to 1.15.6 in /samples/react/WebApp by @dependabot in #736
- Check if the field is writeable when mapping value to param by @zaniniflz in #735
- Add Http Only Cookie support by @marcelologica in #738
- Update MVCFramework.View.Renderers.Mustache to mORMot2 by @landrix in #748
- Fix crash caused by null value set as a nested record. by @rjantz2 in #750
If no bugs/regressions will be found, this will become the stable version
New Contributors
Full Changelog: v3.4.1-sodium...v3.4.2-magnesium-rc1
DelphiMVCFramework 3.4.1-sodium
What's Changed
👉 More info here
- Mustache Helpers changes for issue #693 by @fastbike in #699
- build(deps): bump @babel/traverse from 7.22.5 to 7.23.2 in /samples/react/WebApp by @dependabot in #705
- build(deps): bump @angular/core from 2.0.0 to 10.2.5 in /samples/angular/webapp by @dependabot in #703
- initial adaptor and example with the Sempare Template Engine. by @darnocian in #711
New Contributors
- @darnocian made their first contribution in #711
Full Changelog: v3.4.0-neon...v3.4.1-sodium
DelphiMVCFramework 3.4.0-neon
What's New in DelphiMVCFramework 3.4.0-neon
👉Full blog post about What's New in DelphiMVCFramework 3.4.0-neon👈
New Contributors
Full Changelog: v3.3.0-fluorine...v3.4.0-neon
DelphiMVCFramework 3.3.0-fluorine
What's New in dmvcframework-3.3.0-fluorine
- ⚡ Support for Delphi 11.3 Alexandria
- ⚡ Ability to use records in swagger param and response attributes Issue 649
- ⚡ Improved Wizard - now it produces commented code to show how to use
ContextEvents
- ⚡ Improved compatibility with Delphi 10.2 Tokyo and older versions (Thanks Mark Lobanov)
- ⚡ Added sample and middleware for Prometheus (using https://github.com/marcobreveglieri/prometheus-client-delphi)
- ⚡ Added
Profiler.LogsOnlyIfOverThreshold
which logs only if over the defined threshold - 🐞 FIX Issue 648 Thanks to sf-spb
- 🐞 FIX Issue 652 Thanks to BssdTS
- 🐞 PR 651 Thanks to Francisco Zanini
What's Changed
- build(deps): bump qs from 6.5.2 to 6.5.3 in /samples/react/WebApp by @dependabot in #633
- build(deps): bump decode-uri-component from 0.2.0 to 0.2.2 in /samples/react/WebApp by @dependabot in #634
- build(deps): bump json5 from 1.0.1 to 1.0.2 in /samples/react/WebApp by @dependabot in #635
- build(deps): bump terser from 4.8.0 to 4.8.1 in /samples/react/WebApp by @dependabot in #636
- build(deps): bump minimatch and react-scripts in /samples/react/WebApp by @dependabot in #637
- build(deps): bump axios from 0.21.1 to 0.21.2 in /samples/react/WebApp by @dependabot in #639
- build(deps): bump follow-redirects from 1.12.1 to 1.15.2 in /samples/react/WebApp by @dependabot in #640
- build(deps): bump ansi-regex from 4.1.0 to 4.1.1 in /samples/react/WebApp by @dependabot in #641
- Add the ability to use records in swagger param and response attributes. by @rjantz2 in #649
- Fixed wrong data type for NullableTGUID by @zaniniflz in #651
- build(deps): bump webpack from 5.75.0 to 5.76.1 in /samples/react/WebApp by @dependabot in #650
New Contributors
- @rjantz2 made their first contribution in #649
- @zaniniflz made their first contribution in #651
Full Changelog: dmvcframework-3.2.3-radium...v3.3.0-fluorine
DelphiMVCFramework 3.2.3-radium
What's New in 3.2.3-radium
-
⚡ Default error responses contains the official "reason string" associated to the HTTP status code (this can be a breaking change for some generic client which doesn't correctly interpret the http status code)
-
⚡ Added static method
HTTP_STATUS.ReasonStringFor(HTTPStatusCode)
wich returns the standardReasonString
for a given HTTP status code. -
⚡ Improved handling of
TMVCErrorResponse
information -
⚡ mid-air-collision handling now uses SHA1 instead of MD5
-
⚡ Added
MVCFramework.Commons.MVC_HTTP_STATUS_CODES
const array containing all the HTTP status codes with itsReasonString
. -
⚡ Support for
TObject
descendants in JSONRPC APIs (not only for JSONObject and JSONArray). -
⚡ New global configuration variable
MVCSerializeNulls
.- When MVCSerializeNulls = True (default) empty nullables and nil are serialized as json null.
- When MVCSerializeNulls = False empty nullables and nil are not serialized at all.
-
⚡ Nullable types now have
Equal
method support, the new methodTryHasValue(out Value)
works likeHasValue
but returns the contained value if present. Also there is a better "equality check" strategy. -
⚡ Unit tests now are always executed for Win32 and Win64 bit (both client and server).
-
⚡ Added
TMVCActiveRecord.Refresh
method -
⚡ Unit test suites generates one NUnit XML output file for each platform
-
⚡ New built-in profiler (usable with Delphi 10.4+) - to profile a block of code, write the following
procedure TMyController.ProfilerSample1; begin NotProfiled(); //this line is not profiled //the following begin..end block will be profiled //timing will be saved in a "profiler" log begin var lProf := Profiler.Start(Context.ActionQualifiedName); DoSomething(); DoSomethingElse(); Render('Just executed ' + Context.ActionQualifiedName); end; // profiler writes automatically to the log NotProfiled(); //this line is not profiled end; procedure TMyController.DoSomething; begin begin var lProf := Profiler.Start('DoSomething'); Sleep(100); end; end; procedure TMyController.DoSomethingElse; begin begin var lProf := Profiler.Start('DoSomethingElse'); Sleep(100); DoSomething(); end; end; procedure TMyController.NotProfiled; begin Sleep(100); end;
The log contains the following lines - check the caller/called relationship shown using
>>
and<<
and the deep level[>>][ 1][MainControllerU.TMyController.ProfilerSample1] [profiler] [ >>][ 2][DoSomething] [profiler] [ <<][ 2][DoSomething][ELAPSED: 00:00:00.1088214] [profiler] [ >>][ 2][DoSomethingElse] [profiler] [ >>][ 3][DoSomething] [profiler] [ <<][ 3][DoSomething][ELAPSED: 00:00:00.1096617] [profiler] [ <<][ 2][DoSomethingElse][ELAPSED: 00:00:00.2188468] [profiler] [<<][ 1][MainControllerU.TMyController.ProfilerSample1][ELAPSED: 00:00:00.3277806] [profiler]
To get more info check the "profiling" example.
All profiler logs are generated with a log level
info
. If measured time is greater thanWarningThreshold
the log level iswarning
.WarningThreshold
is expressed in milliseconds and by default is equals to 1000. -
⚡ New
Context
property namedActionQualifiedName
which contains the currently executed action in the formUnitName.ClassName.ActionName
. It is available where theContext
property is available. Obviously is not available in theOnBeforeRouting
middleware events. -
⚡ Added ObjectPool and IntfObjectPool (and related unit tests). Thanks to our sponsor Vivaticket S.p.A.
-
⚡ Method
procedure Render(const AErrorCode: Integer; const AErrorMessage: string = '' ...
has been renamed toRenderStatusMessage
with a better parameter names. -
⚡
IMVCJSONRPCExecutor
supports async call. Thanks to our sponsor Orion Law. Check the new Async sample insamples\jsonrpc_with_published_objects\
. -
⚡ Removed
foTransient
ifTMVCActiveRecord
FieldOptions
. It became obsolete after introduction offoReadOnly
andfoWriteOnly
. -
⚡ Improved
TMVCActiveRecordMiddleware
. Now it can handle multiple connections for the same request. Also, you can completely omit the 'default' connection and just specify wich connection you want to use before starting to create yourTMVCActiveRecord
inherited entities.
Bug Fix in 3.2.3-radium
- Fixed a rendering problem in swagger interface format in case of specific JSON structure
- Fix issue 594 (Thanks to biware-repo)
- Fix issue 595
- Fix issue 590
- Fix issue 490
- Fix Issue 583 (Thanks to Marcelo Jaloto)
- Fix Issue 585
More details about dmvcframework-3.2.3-radium fixes here
DelphiMVCFramework 3.2.2-nitrogen
Overview of DelphiMVCFramework 3.2.2-nitrogen
This version introduces new features in many different areas (swagger, server side view, MVCActiveRecord, renders etc.) so there isn't a single-big-feature to talk about. This version contains also a good number of bugfixes. It's not a critical updated, but this is the best version ever (at least, so far...) and is the suggested version for starting new projects. Enjoy!
What's new in DelphiMVCFramework-3.2.2-nitrogen
-
⚡New! Support for Delphi 11.x Alexandria
-
⚡New
TMVCRESTClient
implementation based on *Net components, the previous one was based on INDY Components (thanks to João Antônio Duarte). -
⚡New!
MVCJSONRPCAllowGET
attribute allows a remote JSON-RPC published object, or a specific method, to be called using GET HTTP Verb as well as POST HTTP Verb. POST is always available, GET is available only if explicitly allowed.IMVCJSONRPCExecutor
allows to specify which HTTP Verb to use when call the server JSON-RPC methods. The default verb can be injected in the constructor and eachExecuteRequest
/ExecuteNotification
allows to override od adhere to the instance default. -
⚡New! eLua server side view support added! The View engine requires Lua's dlls so it is not included in the main package but in a sampl project. Check
serversideviews_lua
sample. -
✅ Improved! Under some heavy load circumnstances the logger queue can get full. Now
TThreadSafeQueue
class uses a cubic function instead of a linear one to wait in case of very high concurrency. This allows a better resiliency in case of high load. -
✅ Improved internal architecture of custom type serializers in case of dynamic linked packages.
-
✅ Improved Swagger/OpenAPI support for System Controllers and improved support for param models.
-
⚡New
TMVCLRUCache
implementation. Very efficient implementation of LRU cache borrowed directly from DMSContainer -
⚡New
TMVCRedirectMiddleware
to handle HTTP redirections in a very simple and flexible way. -
⚡New!
TMVCActiveRecord
supports XML field type in PostgreSQL (in addition to JSON and JSONB). -
⚡New
OnContextCreate
andOnContextDetroyed
events forTMVCEngine
. -
⚡New! Added parameter
RootNode
inBodyFor
andBodyForListOf<T>
methods, just like theBodyAs*
methods. -
⚡New! Added
NullableTGUID
inMVCFramework.Nullables.pas
. -
⚡New
property CustomIntfObject: IInterface
inTWebContext
. This property can be used to inject custom services factory.procedure TMyWebModule.WebModuleCreate(Sender: TObject); begin FMVC := TMVCEngine.Create(Self, procedure(Config: TMVCConfig) begin //configuration code end); FMVC.AddController(TMyController); FMVC.OnWebContextCreate( procedure(const CTX: TWebContext) begin CTX.CustomIntfObject := TServicesFactory.Create; //implements an interface end); FMVC.OnWebContextDestroy( procedure(const CTX: TWebContext) begin //do nothing here end); end;
-
✅ Added parameter to set local timeStamp as UTC.
-
✅ Improved OpenAPI (Swagger) support.
-
✅ Improved Support for OpenAPI (Swagger) API Versioning (check
swagger_api_versioning_primer
sample) -
✅ Improved! The unit tests fully test PostgreSQL, FirebirdSQL and SQLite while testing MVCActiveRecord framework. The other engines are tested using
activerecord_showcase
sample project. -
✅ Improved! MVCActiveRecord doeas a better job to handle TDate/TTime/TDateTime types for SQLite (it is automatic because SQLite doesn't support date/time types).
-
✅ Improved! PostgreSQL, FirebirdSQL, Interbase and SQLite now support tablename and fields with spaces.
-
✅ Improved Nullable Types. Now it's possible to assign
nil
to a nullable type and to check its state using the new propertyIsNull
which is the negation of the already available propertyHasValue
. -
✅ Improved! Now
TMVCStaticFileMiddleware
is able to manage high-level criteria to show/hide/mask specific files in the documetn web root. Check Issue 548 and the updated samplesamples\middleware_staticfiles\
for more info. -
✅ Improved! In case of multiple MVCPath, Swagger consider only the first one (Thanks to V. Ferri and our sponsors)
-
⚡New! Mechanism to customize the JWT claims setup using the client request as suggested in issue495
-
⚡New! Added
TMVCActiveRecord.Merge<T>(CurrentListOfT, ChangesOfT)
to allow merge between two lists ofTMVCActiveRecord
descendants usingUnitOfWork
design pattern. Check the button "Merge" in demo "activerecord_showcase". -
⚡ New! Added default filtering for
TMVCActiveRecord descendants
(more info ASAP) -
⚡ New! Serialization and Deserialization for Pascal
set
(Thanks to rshuck for his suggestions) -
⚡ New! Added partitioning for
TMVCActiveRecord descendants
(more info ASAP) -
✅ Dramatically improved all "JSON-To-DataSet" operations (1 order of magnitude c.a.). Thanks to MPannier and David Moorhouse for their detailed analysis - More info here.
-
✅ Improved! After a big refactoring ("I love to delete code" -- cit. Daniele Teti), support a new SQLGenerator is just 2 (two) methods away! Just as example, this is the current version of
TMVCSQLGeneratorPostgreSQL
type TMVCSQLGeneratorPostgreSQL = class(TMVCSQLGenerator) protected function GetCompilerClass: TRQLCompilerClass; override; public function CreateInsertSQL( const TableName: string; const Map: TFieldsMap; const PKFieldName: string; const PKOptions: TMVCActiveRecordFieldOptions): string; override; function GetSequenceValueSQL(const PKFieldName: string; const SequenceName: string; const Step: Integer = 1): string; override; end;
-
⚡ New! Added new default parameter to
TMVCActiveRecord.RemoveDefaultConnection
andTMVCActiveRecord.RemoveConnection
to avoid exceptions in case of not initialized connection. -
⚡ New! Added the new
MVCOwned
attribute which allows to auto-create nested objects in the deserialization phase. This will not change the current behavior, you ned to explocitly define a property (or a field) asMVCOwned
to allows the serialization to create or destroy object for you. -
✅ Improved!
Context.Data
property is now created on-demand using a lazy loading approach (expect an overall speed improvement). -
✅ Added
LogException
function inMVCFramework.Logger.pas
to easily log exception in standard way. -
✅ Improved
MVCAREntitiesGenerator
project - now it can better handle border cases, field names which collide with Delphi keywords and a big number of tables. -
✅ Improved error handling for JSON-RPC APIs (Thanks to David Moorhouse). More info here.
-
✅ Improved parameter handling for enum and set in JSON-RPC APIs.
-
⚡ New! Added
ActiveRecordConnectionRegistry.AddDefaultConnection(const aConnetionDefName: String)
. The connection definition must be known by FireDAC. This method simplifies the most common scenario shown below.ActiveRecordConnectionRegistry.AddDefaultConnection('MyConDefName'); try //Use active record classes finally ActiveRecordConnectionRegistry.RemoveDefaultConnection; end;
-
⚡ New! Added
ToJSONObject
andToJSONArray
to theIMVCRESTResponse
. These methods automatically parse the response body and return aTJSONObject
or aTJSONArray
respectively. These methods work as a factory - the client code need to handle returned istances. Is the body is not compatible with the request (a.k.a. is not a JSONObject in case ofToJSONObject
, or is not a JSONArray in case ofToJSONArray
) an exception is raised. -
⚡ New! Added support for primary GUID (UUID) primary keys, attributes and serialization (More info at issue 552). Thanks to Marcelo Jaloto for its important collaboration.
-
⚡ New! Added
TMVCJWTBlackListMiddleware
to allow black-listing and (a sort of) logout for a JWT based authentication. This middleware must be registered after theTMVCJWTAuthenticationMiddleware
.This middleware provides 2 events named:
OnAcceptToken
(invoked when a request contains a token - need to returns true/false if the token is still accepted by the server or not) andOnNewJWTToBlackList
(invoked when a client ask to blacklist its current token). There is a new sample available which shows the funtionalities:samples\middleware_jwtblacklist
. -
⚡New!
MVCFromBody
attribute, useful to automatically inject the request body as actual object in the action paramaters. For instance in the following action the body request is automatically deserialized as an object of class TPerson.//interface [MVCHTTPMethod([httpPOST])] [MVCPath('/people')] procedure CreatePerson(const [MVCFromBody] Person: TPerson); //implementation procedure TRenderSampleController.CreatePerson(const Person: TPerson); begin //here you can directly use Person without call Context.Request.BodyAs<TPerson> //the Person object lifecycle is automatically handled by dmvcframework, so don't destroy //If the request body ...
DelphiMVCFramework 3.2.1-carbon
Overview of DelphiMVCFramework 3.2.1-carbon
This version is the version referenced by the DelphiMVCFramework - The Official Guide book (available in english, portuguese and spanish).
This version introduces new features in many different areas (swagger, server side view, MVCActiveRecord, renders etc.) so there isn't a single-big-feature to talk about. This version contains also a good number of bugfixes. It's not a critical updated, but this is the best version ever (at least, so far...) and is the suggested version for starting new projects. Enjoy!
Improvements
-
New
Context: TWebContext
parameter in JSON-RPC Hooks{ Called before any actual routing } procedure OnBeforeRoutingHook(const Context: TWebContext; const JSON: TJsonObject); { Called after routing and before the actual remote method invocation } procedure OnBeforeCallHook(const Context: TWebContext; const JSON: TJsonObject); { Called after actual remote method invocation, even if the method raised an exception } procedure OnAfterCallHook(const Context: TWebContext; const JSON: TJsonObject);
-
When a JSON-RPC Request returns a
System.Boolean
theresult
will be a JSONtrue
orfalse
and no1
or0
as it was in the3.2.0-boron
. -
IMVCJSONRPCExecutor.ExecuteNotification
returns aIJSONRPCResponse
. In case of error response contains information about the error, in case of successful execution the response is a Null Object. -
New React demo (Thanks to Angelo Sobreira da Silva)
-
Serialization support for
TList<Integer>
,TList<String>
,TList<Boolean>
and for allTList<T>
of simple types. -
Added method
MetadataAsJSONObject(FieldNameCase: TMVCNameCase = ncLowerCase): TJSONObject;
inTDataSetHelper
. This method returns the dataset field definitions. While this is valid only for Delphi datasets, can be useful to describe a dataset to a Delphi client.{ "fielddefs": [ { "datatype": 3, "displayname": "ID", "fieldname": "id", "precision": 0, "size": 0 }, { "datatype": 24, "displayname": "CODE", "fieldname": "code", "precision": 0, "size": 5 }, { "datatype": 24, "displayname": "DESCRIPTION", "fieldname": "description", "precision": 0, "size": 200 }, { "datatype": 37, "displayname": "PRICE", "fieldname": "price", "precision": 18, "size": 2 } ] }
The static method
class procedure TFireDACUtils.CreateDatasetFromMetadata( AFDMemTable: TFDMemTable; AMeta: TJSONObject);
gets the previous structure and initialize the fields ofAFDMemTable
with it. When a TFDMemTable is initialized using this approach, the data can be directly loaded from a jsonarray of jsonobject with the same field names. WARNING: This mechanism works only for Delphi clients. Check samplearticles_crud_vcl_client_meta.dproj
to understand the involved mechanisms. -
Added
foReadOnly
andfoWriteOnly
as field options inMVCTableField
attribute (used byTMVCActiveRecord
). Currently available field options are:- foPrimaryKey { it's the primary key of the mapped table }
- foAutoGenerated { not written, read - similar to foReadOnly but is reloaded after insert and update }
- foTransient { never stored nor read - managed only at run-time }
- foReadOnly { not written, read }
- foWriteOnly { written, not read }
Now it is possible to declare entities like the followings (or with any other combinations):
[MVCNameCase(ncLowerCase)] [MVCTable('articles')] TArticleWithWriteOnlyFields = class(TCustomEntity) private [MVCTableField('ID', [foPrimaryKey, foAutoGenerated])] fID: NullableInt32; [MVCTableField('description', [foWriteOnly])] fDescription: string; [MVCTableField('price', [foWriteOnly])] fPrice: Integer; public property ID: NullableInt32 read fID write fID; property Description: string read fDescription write fDescription; property Price: Integer read fPrice write fPrice; end; [MVCNameCase(ncLowerCase)] [MVCTable('articles')] TArticleWithReadOnlyFields = class(TCustomEntity) private [MVCTableField('ID', [foPrimaryKey, foReadOnly])] fID: NullableInt32; [MVCTableField('code', [foTransient])] fCode: NullableString; [MVCTableField('description', [foReadOnly])] fDescrizione: string; [MVCTableField('price', [foReadOnly])] fPrice: Currency; public property ID: NullableInt32 read fID write fID; property Code: NullableString read fCode write fCode; property Description: string read fDescription write fDescription; property Price: Currency read fPrice write fPrice; end;
-
Added the ability to deserialize an object, or alist of objects, starting from an arbitrary node in the JSON (or other format) present in the request body. Works for
BodyAs<T>
and forBodyAsListOf<T>
(Thanks to Raphaël Emourgeon for theBodyAsListOf<T>
implementation).procedure TBooksController.CreateBook; var lBook: TBook; begin //this call deserialize a TBook instance //starting from the 'book' node of //the request body lBook := Context.Request.BodyAs<TBook>('book'); try lBook.Insert; Render201Created('/api/books/' + lBook.ID.ToString); finally lBook.Free; end; end;
-
Improved the primary key type handling for manual handling in MVCActiveRecord.
procedure TMyBaseEntity.OnBeforeInsert; begin inherited; //regardless the name of the PK field //the following code fills the PK with a GUID //Inheriting the other entities from this, all //will inherit this behavior. SetPK(TValue.From<NullableString>(TGUID.NewGuid.ToString)); //if the PK was a simple string, the code //should be like the following //SetPK(TGUID.NewGuid.ToString); end;
-
Improved
activerecord_showcase
sample. -
Improved
TMVCStaticFilesMiddleware
. Now is able to correctly serve SPA applications from any subfolder. -
Added property
Context.HostingFrameworkType
. This property is of typeTMVCHostingFrameworkType
and can assume one of the following values:hftIndy
(if the service is using the built-in Indy HTTP server) ,hftApache
(if the project is compiled as Apache module) orhftISAPI
(if the project is compiled as ISAPI module). -
TMVCNameCase
got the newncSnakeCase
among the possibles casing (thanks to João Antônio Duarte for its work in this area).Now the
TMVCNameCase
declaration is the following:TMVCNameCase = (ncAsIs, ncUpperCase, ncLowerCase, ncCamelCase, ncPascalCase, ncSnakeCase);
Here's how the new
ncSnakeCase
worksOriginal SnakeCase OneTwo one_two One__Two one_two OneTwo03 one_two_03 -
Support for Mustache partials (Thanks to David Moorhouse and his work about issue 221). Sample \samples\serversideviews_mustache has been updated to show how to use partials.
-
Added dynamic properties access to
TMVCActiveRecord
descendants. Indexed propertyAttributes
is index using the property name and set/get aTValue
representing the property value.procedure TMainForm.btnAttributesClick(Sender: TObject); var lCustomer: TCustomer; lID: Integer; begin lCustomer := TCustomer.Create; try lCustomer.Attributes['CompanyName'] := 'Google Inc.'; lCustomer.Attributes['City'] := 'Montain View, CA'; lCustomer.Attributes['Note'] := 'Hello there!'; lCustomer.Attributes['Code'] := 'XX123'; lCustomer.Attributes['Rating'] := 3; lCustomer.Insert; lID := lCustomer.ID; finally lCustomer.Free; end; lCustomer := TMVCActiveRecord.GetByPK<TCustomer>(lID); try Assert('Google Inc.' = lCustomer.Attributes['CompanyName'] .AsType<NullableString>().Value); Assert('Montain View, CA' = lCustomer.Attributes['City'].AsString); Assert('XX123' = lCustomer.Attributes['Code'] .AsType<NullableString>().Value); Assert('Hello there!' = lCustomer.Attributes['Note'].AsString); lCustomer.Update; finally lCustomer.Free; end;
-
Breaking Change!
TMVCStaticFileMiddleware
cannot be registered to "/" anymore- The suggested solution is to create a simple redirection controller which redirect "/" to the proper path (check this example).
-
Breaking Change!
DocumentRoot
ofTMVCStaticFileMiddleware
must be a valid folder. IfDocumentRoot
doesn't exist an exception is raised.
Bug Fixes
-
Fix for issue 421
-
Fix for [issue 424](https://github.com/dani...
DelphiMVCFramework 3.2.0-boron
DelphiMVCFramework 3.2.0-boron
-
New! Support for Delphi 10.4 Sydney!
-
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 rendering Spring4D Nullable Types - (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! Added support out operator in RQL parser. The RQL out operator is equivalent to the SQL NOT IN operator.
- New!
TMVCAnalyticsMiddleware
to do automatic analytics on the API (generates a...
DelphiMVCFramework 3.2.0-boron-RC7
DelphiMVCFramework 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! `TMVCActiv...