diff --git a/README.md b/README.md index 25e97e67..7c2adc28 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ ![DelphiMVCFramework Logo](docs/dmvcframework_logofacebook.png) -# DelphiMVCFramework 3.1.0-lithium is [here](https://github.com/danieleteti/delphimvcframework/releases/tag/v3.1.0-lithium)! -DelphiMVCFramework is the **most popular** Delphi project on GitHub! + +# DelphiMVCFramework 3.2.0-boron is [here](https://github.com/danieleteti/delphimvcframework/releases/tag/v3_2_0_boron)! + +DMVCFramework is a very popular Delphi framework which provides an easy to use, scalable, flexible [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer), [JSON-RPC](https://en.wikipedia.org/wiki/JSON-RPC) and [ActiveRecord](https://www.martinfowler.com/eaaCatalog/activeRecord.html) framework for Delphi developers. DMVCFramework is the **most popular** Delphi project on GitHub and compiles for Windows (32 and 64bit) and Linux (64bit). DMVCFramework services can be compiled as console application, Windows Service, Linux daemon, Apache module (Windows and Linux) and IIS ISAPI (Windows). DMVCFramework works with Delphi 10.4 Sydney, Delphi 10.3 Rio, Delphi 10.2 Tokyo, Delphi 10.1 Berlin, Delphi 10 Seattle. @@ -45,430 +47,121 @@ Congratulations to Daniele Teti and all the staff for the excellent work!" -- Ma ## DMVCFramework Main Features - * Simple to use, check the ["Getting Started: 5 minutes guide"](https://danieleteti.gitbooks.io/delphimvcframework/content/chapter_getting_started.html) (it is quite old) and you will be up and running in 5 minutes or less! (the guide is a bit outdated - a new book is being written by Daniele Teti. Let us know if you are interested in [DMVCFramework Handbook](https://leanpub.com/delphimvcframework)) + * DMVCFramwork is simple to use, has a [lot of examples](https://github.com/danieleteti/delphimvcframework/tree/master/samples), is documented and there are a [lot of tutorials](https://www.youtube.com/results?search_query=delphimvcframework) available. + + * A new book is being written by Daniele Teti. Let us know if you are interested in [DMVCFramework Handbook](https://leanpub.com/delphimvcframework)) + * [Project Roadmap](roadmap.md) is always public + * More than 40 samples to learn all the features and be proficient and productive + * Commercially supported by [bit Time Professionals](http://www.bittimeprofessionals.it) (training, consultancy, custom development etc.) - * RESTful (RMM Level 3) compliant - * JSON-RPC 2.0 Support with automatic objects remotization (check [sample](https://github.com/danieleteti/delphimvcframework/tree/master/samples/jsonrpc_with_published_objects)) - * Stable and solid, used by small/mid/big projects since 2010 - * Very fast! (2.x was pretty fast and now, 3.x is 60% faster than the 2.x!) - * Support group at https://www.facebook.com/groups/delphimvcframework with more than 2700 active members - * Can be used in load balanced environment - * Wizard for the Delphi IDE. It makes DelphiMVCFramework even more simple to use! - * Optional session support - * JSON Web Token Support (JWT) (check [sample](https://github.com/danieleteti/delphimvcframework/tree/master/samples/jsonwebtoken)) - * Extendable using middleware - simple hooks to handle request/response (check [sample](https://github.com/danieleteti/delphimvcframework/tree/master/samples/middleware)) - * Flexible yet simple to use, authorization/authentication framework based on industry standards. - * HTTP Basic Authentication - * JWT Authentication - * Custom Authentication + +* RESTful (RMM Level 3) compliant + +* JSON-RPC 2.0 Support with automatic objects remotization (check [sample](https://github.com/danieleteti/delphimvcframework/tree/master/samples/jsonrpc_with_published_objects)) + +* DMVCFramework' MVCActiveRecord allows an easy and fast database access. + +* Stable and solid, used by small/mid/big projects since 2010 + +* Very fast! (2.x was pretty fast and now, 3.x is 60% faster than the 2.x!) + +* Support group at https://www.facebook.com/groups/delphimvcframework with more than 2700 active members + +* Can be used in load balanced environment + +* Wizard for the Delphi IDE. It makes DelphiMVCFramework even more simple to use! + +* Optional session support + +* JSON Web Token Support (JWT) (check [sample](https://github.com/danieleteti/delphimvcframework/tree/master/samples/jsonwebtoken)) + +* Extendable using middleware - simple hooks to handle request/response (check [sample](https://github.com/danieleteti/delphimvcframework/tree/master/samples/middleware)) + +* Flexible yet simple to use, authorization/authentication framework based on industry standards. + * HTTP Basic Authentication + * JWT Authentication + * Custom Authentication + * CORS support - * Controllers inheritance! You can define your own base controller and inherit from it. - * Fancy URL with parameter mappings - * Specialized renders to generate text, HTML, JSON. - * Powerful and customizable mapper to serialize/deserialize data. - * Can be packaged as stand alone server, apache module (XE6 or better) and ISAPI dll - * Integrated REST Client - * Works on Linux (Delphi 10.2 Tokyo or better) - * Completely unit tested (more than 170 unit tests) - * There is a sample for each functionality (check the [dmvcframework_(yourversion)_samples.zip](https://github.com/danieleteti/delphimvcframework/releases)) - * Server side generated pages using [Mustache for Delphi](https://github.com/synopse/dmustache) or [TemplatePro](https://github.com/danieleteti/templatepro) - * Specific trainings are available (email to `professionals@bittime.it` for a date and a place) - * Push notifications support using [ServerSentEvents](https://github.com/danieleteti/delphimvcframework/tree/master/samples/serversentevents) - * Automatic documentation through `/system/describeserver.info` - * Driven by its huge community (Facebook group https://www.facebook.com/groups/delphimvcframework) - * Semantic Versioning - * Simple and [documented](docs/ITDevCON%202013%20-%20Introduction%20to%20DelphiMVCFramework.pdf) - * Continuously tested for Delphi versions incompatibilities by the proud [compatibility maintainers](COMPATIBILITY_MANTAINERS.MD) -## What's Cooking in the Lab +* Controllers inheritance! You can define your own base controller and inherit from it. -### DelphiMVCFramework 3.2.0-boron (currently in `RC` phase) +* Fancy URL with parameter mappings -> WARNING! Considering the huge amount of features added in 3.1.1-beryllium during its RC phase, the dmvcframework-3.1.1-beryllium has been renamed to dmvcframework-3.2.0-boron +* Specialized renders to generate text, HTML, JSON. -- New! Support for Delphi 10.4 Sydney! +* Powerful and customizable mapper to serialize/deserialize data. + +* Can be packaged as stand alone server, apache module (XE6 or better) and ISAPI dll + +* Integrated REST Client + +* Works on Linux (Delphi 10.2 Tokyo or better) + +* Completely unit tested (more than 170 unit tests) + +* There is a sample for each functionality (check the [dmvcframework_(yourversion)_samples.zip](https://github.com/danieleteti/delphimvcframework/releases)) + +* Server side generated pages using [Mustache for Delphi](https://github.com/synopse/dmustache) or [TemplatePro](https://github.com/danieleteti/templatepro) + +* Specific trainings are available (email to `professionals@bittime.it` for a date and a place) + +* Push notifications support using [ServerSentEvents](https://github.com/danieleteti/delphimvcframework/tree/master/samples/serversentevents) + +* Automatic documentation through `/system/describeserver.info` + +* Driven by its huge community (Facebook group https://www.facebook.com/groups/delphimvcframework) + +* Semantic Versioning + +* Simple and [documented](docs/ITDevCON%202013%20-%20Introduction%20to%20DelphiMVCFramework.pdf) + +* Continuously tested for Delphi versions incompatibilities by the proud [compatibility maintainers](COMPATIBILITY_MANTAINERS.MD) + +## Overview of DelphiMVCFramework 3.2.0-boron + +> More detailed info about DMVCFramework 3.2.0-boron in the *Release Notes* Section - 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` and `ncPascalCase` 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](https://github.com/joaoduarte19) and [Geoffrey Smith](https://github.com/geoffsmith82)) +- New! Added Swagger support - 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 the `renders.dproj` sample. Here's some example of the shining new `ObjectDict()` +- 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 the `renders.dproj` sample. -**Example 1: Rendering a list of objects not freeing them after rendering** + > ObjectDict is the suggested way to renders data. However, the other ones are still there and works as usual. -*Classic* - -```delphi -procedure TRenderSampleController.GetLotOfPeople; -begin - Render(GetPeopleList, False); -end; - -``` - -*New approach with ObjectDict* - -```delphi -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* - -```delphi -procedure TRenderSampleController.GetLotOfPeople; -begin - Render(GetPeopleList); -end; - -``` - -*New approach with ObjectDict* - -```delphi -procedure TRenderSampleController.GetLotOfPeople; -begin - Render(ObjectDict().Add('data', GetPeopleList)); -end; - -``` - -**Example 3: Rendering a list of objects adding links for HATEOAS support** - -*Classic* - -```delphi -procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS; -var - p: TPerson; - People: TObjectList; -begin - People := TObjectList.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(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* - -```delphi -procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS; -var - p: TPerson; - People: TObjectList; -begin - People := TObjectList.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, StrDict and nil) at the same time using different casing, HATEOAS callbacks and modes. - -```delphi -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 and improved! SQLGenerator and RQL compiler for PostgreSQL, SQLite, MSSQLServer, MySQL, MariaDB, Firebird and Interbase. - New! *MVCNameAs* attribute has got a new parameter named `Fixed` (default: false). If `Fixed` is true, then the name is not processed by the `MVCNameCase` 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](https://github.com/joaoduarte19)). -- New! Added support for Spring4D Nullable Types serialization - check sample (thanks to [João Antônio Duarte](https://github.com/joaoduarte19)). -- New! Added `OnRouterLog` event to log custom information for each request (thanks to [Andrea Ciotti](https://github.com/andreaciotti) for the first implementation and its PR). - New! Optionally load system controllers (those who provide `/describeserver.info`, `/describeplatform.info` and `/serverconfig.info` system actions) setting `Config[TMVCConfigKey.LoadSystemControllers] := 'false';` in the configuration block. - Improved! Now the router consider `Accept:*/*` compatible for every `MVCProduces` values - Improved! Greatly improved support for [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) in renders. Check `TRenderSampleController.GetPeople_AsObjectList_HATEOS` and all the others actions end with `HATEOS` in `renders.dproj` sample) -```delphi -//Now is really easy to add "links" property automatically for each collection element while rendering -Render(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 class `TCustomer`) - Change! `TMVCACtiveRecord.GetByPK` raises an exception by default if the record is not found - optionally can returns `nil` using new parameter `RaiseExceptionIfNotFound` - 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 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 an `RQL` expression as `where` clause. -- New! `TMVCActiveRecord.Store` which automatically executes Insert or Update considering primary key value. +- New! `TMVCActiveRecord.DeleteAll`, `TMVCActiveRecord.DeleteRQL` and `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](http://www.bittimeprofessionals.it/prodotti/dmscontainer)*) -- New! SQLite support in `MVCActiveRecord` and RQL, so that `MVCActiveRecord` can be used also for Delphi mobile projects! -- Default JSON Serializer can verbatim pass properties with type `JsonDataObjects.TJSONObject` without using `string` 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's `TFieldType` enumeration). -- Improved! In case of unhandled exception `TMVCEngine` is compliant with the default response content-type (usually it did would reply using `text/plain`). -- Added! New overloads for all the Log\* calls. Now it is possible to call `LogD(lMyObject)` to get logged `lMyObject` 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. -```delphi -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](https://github.com/fastbike)). Sample "custom_exception_handling" show how to use it. -- Improved! Exceptions rendering while using MIME types different to `application/json`. +- New! Custom Exception Handling. Sample "custom_exception_handling" show how to use it. - SSL Server support for `TMVCListener` (Thanks to [Sven Harazim](https://github.com/landrix)) - Improved! Datasets serialization speed improvement. In some case the performance [improves of 2 order of magnitude](https://github.com/danieleteti/delphimvcframework/issues/205#issuecomment-479513158). (Thanks to https://github.com/pedrooliveira01) -- New! Added `in` operator in RQL parser (Thank you to [João Antônio Duarte](https://github.com/joaoduarte19) for his initial work on this) - New! Added `TMVCActiveRecord.Count(RQL)` to count record based on RQL criteria. -- New! Added in `TMVCActiveRecordController` new `count` parameter. When in a "Get List" request is used a RQL filter, sending a "count=true" parameter, the response will contains also the count of the record matching filter. -- New! `TMVCActiveRecord` can handle non autogenerated primary key. -- New! `TMVCActiveRecord` support runtime table name change (Thanks to [ads69](https://github.com/ads69)) -- 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`, `TArray` and `TArray` 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! -- Improved! Better exception handling in `OnBeforeDispatch` (Thanks to [Spinettaro](https://github.com/spinettaro)) +- Better JWT Standard Compliance! - New! `StrToJSONObject` function to safely parse a string into a JSON object. - New! Serialization callback for custom `TDataSet` descendants serialization in `TMVCJsonDataObjectsSerializer`. -```delphi -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](https://github.com/joaoduarte19)). - - It is now possible to deserialize a generic class like this: - - ```delphi - TGenericEntity = class - private - FCode: Integer; - FItems: TObjectList; - 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 read FItems write FItems; - end; - ``` - - Before it was not possible because you should add the `MVCListOf` attribute to the `TObjectList` type property. - -- New! Added serialization support for (thanks to [dockerandy](https://github.com/dockerandy) for his initial work) - - - `TArray` - - `TArray` - - `TArray` - - `TArray` - -- New! The **MVCAREntitiesGenerator** can optionally register all the generated entities also in the `ActiveRecordMappingRegistry` (Thanks to [Fabrizio Bitti](https://twitter.com/fabriziobitti) from [bit Time Software](http://www.bittime.it)) - -- Compression through `TMVCCompressionMiddleware` is not enabled is DMVCFramework service is compiled as Apache module or IIS ISAPI. The host webserver should handle the actual compression. +- Added de/serializing iterables (e.g. generic lists) support without `MVCListOf` attribute. - New! Children objects lifecycle management in `TMVCActiveRecord` (methods `AddChildren` and `RemoveChildren`). 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. @@ -690,40 +383,6 @@ end; - Fixed! [issue388](https://github.com/danieleteti/delphimvcframework/issues/388) - 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.* -## DelphiMVCFramework 3.1.0-lithium (Current Release) - -- New! Added `TMVCActiveRecord` framework (check sample `activerecord_showcase` and `activerecord_crud`) -- New! Added `TMVCActiveRecordController` (check sample `activerecord_crud`) -- Automatic permissions handling for `TMVCActiveRecordController` (check sample `activerecord_crud`) -- EntityProcessor for `TMVCActiveRecordController` (check sample `activerecord_crud`) -- `Config[TMVCConfigKey.FallbackResource]` is served only if request path is empty or `/`. -- New! Now the JSON-RPC executor provides methods to handle HTTP headers for JSON-RPC requests and notifications. -- `TDataSetHolder` is a new render able to render a dataset with a set of custom metadata (eg `count`,`page` etc). Check [issue #137](https://github.com/danieleteti/delphimvcframework/issues/137) -- `404` and `500` status code returns always a `text/plain` content-type -- Refactored ISAPI sample -- Speed improvement! Removed enhanced visibility for action methods. Now only public and published methods can be used as actions. -- `TMVCController.Create` is `virtual`! Now on your base controllers can be even more powerful! -- New! Added `MAX_REQUEST_SIZE` for limiting the size of the incoming HTTP requests. IDE Expert is updated too! -- New! Added method `TMVCJsonDataObjectsSerializer.ListToJsonArray` -- New! `TMVCResponse` for handle generic (non error) response -- New! `TMVCErrorResponse` for handle generic error response -- New! Added class `TMVCActiveRecordList` used in the manual `TMVCActiveRecord` programming -- New! Added `gzip` compression support in addition to `deflate` in `TCompressionMiddleware` -- FIX for [issue #143](https://github.com/danieleteti/delphimvcframework/issues/143) -- FIX for [issue #141](https://github.com/danieleteti/delphimvcframework/issues/141) -- Removed deprecated methods in `IRESTResponse` -- FIX misspelled header name in `IRESTResponse` -- New! Added `gzip` and `deflate` support in `TRestClient` when reading responses -- `TCompressionMiddleware` has been renamed in `TMVCCompressionMiddleware` -- New! `TMVCCompressionMiddleware` is added by IDE Expert by default -- Removed the old JSON serializer based on `System.JSON.pas', now the only available JSON serializer is based on [JsonDataObjects](https://github.com/ahausladen/JsonDataObjects) parser (Thank you Andreas Hausladen). -- Changed! Custom Types Serializer *must* be registered by media-type only, without charset definition (e.g. just `application/json` and not `application/json;charset=utf-8`) -- Changed! `IMVCTypeSerializer` is more powerful and simple to use! -- Sending wrongly formatted JSON now returns a more correctly `400 Bad Request` and not `500 Internal Server Error` as in the previous versions -- New! Support for Spring4d nullable types (check `samples\renders_spring4d_nullables`) -- New! `TMVCJSONRPCPublisher` allows to easily expose plain Delphi objects (and even datamodules) through a JSON-RPC 2.0 interface! -- *Breaking Change!* The JSON RPC Client layer is now interface based. - ## Roadmap DelphiMVCFramework roadmap is always updated as-soon-as the features planned are implemented. Check the roadmap [here](roadmap.md). @@ -732,7 +391,602 @@ As you know, good support on open source software is a must for professional use If you need trainings, consultancy or custom developments on DelphiMVCFramework, send an email to *dmvcframework at bittime dot it*. Alternatively you can send a request using the [contacts forms](http://www.bittimeprofessionals.it/contatti) on [bit Time Professionals website](http://www.bittimeprofessionals.it). bit Time Professionals is the company behind DelphiMVCFramework, all the main developers works there. ## Release Notes -#### 3.1.0 (lithium) + +### 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` and `ncPascalCase` 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](https://github.com/joaoduarte19) and [Geoffrey Smith](https://github.com/geoffsmith82)) + +- 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 the `renders.dproj` sample. Here's some example of the shining new `ObjectDict()`. + +**Example 1: Rendering a list of objects not freeing them after rendering** + +*Classic* + +```delphi +procedure TRenderSampleController.GetLotOfPeople; +begin + Render(GetPeopleList, False); +end; + +``` + +*New approach with ObjectDict* + +```delphi +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* + +```delphi +procedure TRenderSampleController.GetLotOfPeople; +begin + Render(GetPeopleList); +end; + +``` + +*New approach with ObjectDict* + +```delphi +procedure TRenderSampleController.GetLotOfPeople; +begin + Render(ObjectDict().Add('data', GetPeopleList)); +end; + +``` + +**Example 3: Rendering a list of objects adding links for HATEOAS support** + +*Classic* + +```delphi +procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS; +var + p: TPerson; + People: TObjectList; +begin + People := TObjectList.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(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* + +```delphi +procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS; +var + p: TPerson; + People: TObjectList; +begin + People := TObjectList.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. + +```delphi +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). If `Fixed` is true, then the name is not processed by the `MVCNameCase` 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](https://github.com/joaoduarte19)) +- New! Added support for rendering Spring4D Nullable Types - (thanks to [João Antônio Duarte](https://github.com/joaoduarte19)) +- New! Added `OnRouterLog` event to log custom information for each request (thanks to [Andrea Ciotti](https://github.com/andreaciotti) for the first implementation and its PR) +- New! Optionally load system controllers (those who provide `/describeserver.info`, `/describeplatform.info` and `/serverconfig.info` system actions) setting `Config[TMVCConfigKey.LoadSystemControllers] := 'false';` in the configuration block. +- Improved! Now the router consider `Accept:*/*` compatible for every `MVCProduces` values. +- Improved! Greatly improved support for [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) in renders. Check `TRenderSampleController.GetPeople_AsObjectList_HATEOS` and all the others actions end with `HATEOS` in `renders.dproj` sample). + +```delphi +//Now is really easy to add "links" property automatically for each collection element while rendering +Render(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 class `TCustomer`) +- Change! `TMVCACtiveRecord.GetByPK` raises an exception by default if the record is not found - optionally can returns `nil` using new parameter `RaiseExceptionIfNotFound` +- 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 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 an `RQL` expression as `where` 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](http://www.bittimeprofessionals.it/prodotti/dmscontainer)*). +- New! SQLite support in `MVCActiveRecord` and RQL, so that `MVCActiveRecord` can be used also for Delphi mobile projects! +- Default JSON Serializer can verbatim pass properties with type `JsonDataObjects.TJSONObject` without using `string` 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's `TFieldType` enumeration). +- Improved! In case of unhandled exception `TMVCEngine` is compliant with the default response content-type (usually it did would reply using `text/plain`). +- Added! New overloads for all the Log\* calls. Now it is possible to call `LogD(lMyObject)` to get logged `lMyObject` 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. +```delphi +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](https://github.com/fastbike)). 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](https://github.com/landrix)). +- Improved! Datasets serialization speed improvement. In some case the performance [improves of 2 order of magnitude](https://github.com/danieleteti/delphimvcframework/issues/205#issuecomment-479513158). (Thanks to https://github.com/pedrooliveira01). +- New! Added `in` operator in RQL parser (Thank you to [João Antônio Duarte](https://github.com/joaoduarte19) for his initial work on this). +- New! Added `TMVCActiveRecord.Count(RQL)` to count record based on RQL criteria. +- New! `TMVCActiveRecord` can handle non autogenerated primary key. +- 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`, `TArray` and `TArray` 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 180+ unit tests that checks its functionalities at each build! +- Improved! Better exception handling in `OnBeforeDispatch` (Thanks to [Spinettaro](https://github.com/spinettaro)) +- New! `StrToJSONObject` function to safely parse a string into a JSON object. +- New! Serialization callback for custom `TDataSet` descendants serialization in `TMVCJsonDataObjectsSerializer`. +```delphi +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](https://github.com/joaoduarte19)). + + It is now possible to deserialize a generic class like this: + + ```delphi + TGenericEntity = class + private + FCode: Integer; + FItems: TObjectList; + 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 read FItems write FItems; + end; + ``` + + Before it was not possible because you should add the `MVCListOf` attribute to the `TObjectList` type property. + +- New! Added serialization support for (thanks to [dockerandy](https://github.com/dockerandy) for his initial work) + + - `TArray` + - `TArray` + - `TArray` + - `TArray` + +- New! The **MVCAREntitiesGenerator** can optionally register all the generated entities also in the `ActiveRecordMappingRegistry` (Thanks to [Fabrizio Bitti](https://twitter.com/fabriziobitti) from [bit Time Software](http://www.bittime.it)) + +- New! Experimental (alpha stage) support for Android servers! + +- New! Children objects lifecycle management in `TMVCActiveRecord` (methods `AddChildren` and `RemoveChildren`). 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. + + ```delphi + //Having the following declaration + + type + [MVCNameCase(ncCamelCase)] + [MVCTable('authors')] + TAuthor = class(TPersonEntityBase) + private + fBooks: TEnumerable; + [MVCTableField('full_name')] + fFullName: string; + function GetBooks: TEnumerable; + public + [MVCNameAs('full_name')] + property FullName: string read fFullName write fFullName; + property Books: TEnumerable read GetBooks; + end; + + + //method GetBooks can be implemented as follows: + + implementation + + function TAuthor.GetBooks: TEnumerable; + begin + if fBooks = nil then + begin + fBooks := TMVCActiveRecord.Where('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 inner `THTTPClient` (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 `/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 + + ```delphi + 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 + + ```delphi + 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](https://www.jsonrpc.org/specification#examples) for more examples. + + - New! JSONRPC Hooks for published objects + + ```delphi + //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); + + ``` + + + +- **Deprecated!** `TDataSetHolder` is deprecated! Use the shining new `ObjectDict(boolean)` instead. + +- Added ability to serialize/deserialize types enumerated by an array of mapped values (Thanks to [João Antônio Duarte](https://github.com/joaoduarte19)). + + ```delphi + 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 the `dmvcframework` 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.4 Sydney | `packages\d104\dmvcframework_group.groupproj` | +| 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` | + +#### Breaking Changes in 3.2.0-boron + +- In `MVCActiveRecord` attribute `MVCPrimaryKey` has been removed and merged with `MVCTableField`, so now `TMVCActiveRecordFieldOption` is a set of `foPrimaryKey`, `foAutoGenerated`, `foTransient` (check `activerecord_showcase.dproj` sample). + +- Middleware `OnAfterControllerAction` are now invoked in the same order of `OnBeforeControllerAction` (previously were invoked in reversed order). + +- `TMVCEngine` is no more responsible for static file serving. If you need static files used the new `TMVCStaticFilesMiddleware` (check the sample). As consequence `TMVCConfigKey.DocumentRoot`, `TMVCConfigKey.IndexDocument` and `TMVCConfigKey.FallbackResource` are no more available. + +- `TMVCEngine.Config` property is now read-only. Can be changed only in the anonymous method injected in the constructor. + + ```delphi + // This is valid + //////////////////////////////////////// + FMVC := TMVCEngine.Create(Self, + procedure(Config: TMVCConfig) + begin + // session timeout (0 means session cookie) + Config[TMVCConfigKey.SessionTimeout] := '0'; + //Other Configurations + end); + FMVC.AddController(TMyController); + + + // This is not valid (exception is raised) + ///////////////////////////////////////// + + FMVC := TMVCEngine.Create(Self);, + FMVC.Config[TMVCConfigKey.SessionTimeout] := '0'; {run-time error here} + FMVC.AddController(TMyController); + + ``` + + + +#### Bug Fixes in 3.2.0-boron + +- Fixed! [issue38](https://github.com/danieleteti/delphimvcframework/issues/38) +- Fixed! [issue184](https://github.com/danieleteti/delphimvcframework/issues/184) +- Fixed! [issue278](https://github.com/danieleteti/delphimvcframework/issues/278) +- Fixed! [issue164](https://github.com/danieleteti/delphimvcframework/issues/164) +- Fixed! [issue182](https://github.com/danieleteti/delphimvcframework/issues/182) +- Fixed! [issue232](https://github.com/danieleteti/delphimvcframework/issues/232) (Thanks to [João Antônio Duarte](https://github.com/joaoduarte19)) +- Fixed! [issue239](https://github.com/danieleteti/delphimvcframework/issues/239) +- Fixed! [issue289](https://github.com/danieleteti/delphimvcframework/issues/289) (Thanks to [João Antônio Duarte](https://github.com/joaoduarte19)) +- Fixed! [issue291](https://github.com/danieleteti/delphimvcframework/issues/291) (Thanks to [João Antônio Duarte](https://github.com/joaoduarte19)) +- Fixed! [issue305](https://github.com/danieleteti/delphimvcframework/issues/305) (Thanks to [João Antônio Duarte](https://github.com/joaoduarte19)) +- Fixed! [issue312](https://github.com/danieleteti/delphimvcframework/issues/312) +- Fixed! [issue330](https://github.com/danieleteti/delphimvcframework/issues/330) +- Fixed! [issue333](https://github.com/danieleteti/delphimvcframework/issues/333) +- Fixed! [issue334](https://github.com/danieleteti/delphimvcframework/issues/334) +- Fixed! [issue336](https://github.com/danieleteti/delphimvcframework/issues/336) +- Fixed! [issue337](https://github.com/danieleteti/delphimvcframework/issues/337) +- Fixed! [issue338](https://github.com/danieleteti/delphimvcframework/issues/338) +- Fixed! [issue239](https://github.com/danieleteti/delphimvcframework/issues/239) +- Fixed! [issue345](https://github.com/danieleteti/delphimvcframework/issues/345) +- Fixed! [issue349](https://github.com/danieleteti/delphimvcframework/issues/349) +- Fixed! [issue350](https://github.com/danieleteti/delphimvcframework/issues/350) +- Fixed! [issue355](https://github.com/danieleteti/delphimvcframework/issues/355) +- Fixed! [issue356](https://github.com/danieleteti/delphimvcframework/issues/356) +- Fixed! [issue362](https://github.com/danieleteti/delphimvcframework/issues/362) +- Fixed! [issue363](https://github.com/danieleteti/delphimvcframework/issues/363) +- Fixed! [issue364](https://github.com/danieleteti/delphimvcframework/issues/364) (Thanks to [João Antônio Duarte](https://github.com/joaoduarte19)) +- Fixed! [issue366](https://github.com/danieleteti/delphimvcframework/issues/366) +- Fixed! [issue376](https://github.com/danieleteti/delphimvcframework/issues/376) (Thanks to [João Antônio Duarte](https://github.com/joaoduarte19)) +- Fixed! [issue379](https://github.com/danieleteti/delphimvcframework/issues/379) (Thanks to [João Antônio Duarte](https://github.com/joaoduarte19) and [Maincosi](https://github.com/maiconsi) for their work) +- 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.* + +### 3.1.0 (lithium) - `TMVCActiveRecord` framework - `TMVCActiveRecordController` with automatic RESTful interface generation and permissions handling @@ -749,21 +1003,21 @@ If you need trainings, consultancy or custom developments on DelphiMVCFramework, - `TMVCJSONRPCPublisher` allows to easily expose plain Delphi objects (and even data modules) through a JSON-RPC 2.0 interface! - *Breaking Change!* The JSON RPC Client layer is now interface based. -#### 3.0.0 (hydrogen) +### 3.0.0 (hydrogen) - First release of the 3.0.0 version -#### 2.1.3 (lithium) +### 2.1.3 (lithium) - FIX https://github.com/danieleteti/delphimvcframework/issues/64 - Added unit tests to avoid regressions -#### 2.1.2 (helium) +### 2.1.2 (helium) - FIX for Delphi versions who don't have ```TJSONBool``` (Delphi XE8 or older) - Added new conditional define in dmvcframework.inc: JSONBOOL (defined for Delphi Seattle+) -#### 2.1.1 (hydrogen) +### 2.1.1 (hydrogen) - Updated the IDE Expert to show the current version of the framework - FIX to the mapper about the datasets null values (needs to be checked in old Delphi versions) @@ -774,60 +1028,41 @@ If you need trainings, consultancy or custom developments on DelphiMVCFramework, ## Samples and documentation DMVCFramework is provided with a lot of examples focused on specific functionality. All samples are in [Samples](samples) folder. -Check the [DMVCFramework Developer Guide](https://danieleteti.gitbooks.io/delphimvcframework/content/) (work in progress). +# Getting Started: 5 minutes guide -# Getting Started -Below the is a basic sample of a DMVCFramework server wich can be deployed as standa-alone application, as an Apache module or as ISAPI dll. This flexibility is provided by the Delphi WebBroker framework (built-in in Delphi since Delphi 4). -The project contains an IDE Expert which make creating DMVCFramework project a breeze. However not all the Delphi version are supported, so here's the manual version (which is not complicated at all). +DMVCFramework allows to create powerful RESTful servers without effort. You can create a full-flagged RESTful server in a couple of clicks. -To create this server, you have to create a new ```Delphi Projects -> WebBroker -> WebServerApplication```. Then add the following changes to the webmodule. -```delphi -unit WebModuleUnit1; +## DelphiMVCFramework 3.2.0-boron Installation -interface +- DMVCFramework *must* be installed using the [Github release](https://github.com/danieleteti/delphimvcframework/releases/tag/v3_2_0_boron). Download the zip file -uses System.SysUtils, System.Classes, Web.HTTPApp, MVCFramework {this unit contains TMVCEngine class}; +- Unzip it the release zip in a folder named `C:\dmvc` (or where you prefer). -type - TWebModule1 = class(TWebModule) - procedure WebModuleCreate(Sender: TObject); - procedure WebModuleDestroy(Sender: TObject); +- Launch RAD Studio and open `C:\dmvc\packages\d104\dmvcframework_group.groupproj` - private - MVC: TMVCEngine; + > WARNING! In the last path shown "d104" is for Delphi 10.4 Sydney. Use the correct package for your Delphi version. - public - { Public declarations } - end; +- Install the package and close all -var - WebModuleClass: TComponentClass = TWebModule1; +- Now, DMVCFramework expert is installed and you are able to create DMVCFramework project. Go to go to `File->New->Other` select `Delphi Project->DMVC->DelphiMVCFramework Project` -implementation + ![](docs/getting_started_10_ide_expert_new_project.png) -{$R *.dfm} - -uses UsersControllerU; //this is the unit where is defined the controller - -procedure TWebModule1.WebModuleCreate(Sender: TObject); -begin - MVC := TMVCEngine.Create(Self); - MVC.Config[TMVCConfigKey.DocumentRoot] := 'public_html'; //if you need some static html, javascript, etc (optional) - MVC.AddController(TUsersController); //see next section to know how to create a controller -end; - -procedure TWebModule1.WebModuleDestroy(Sender: TObject); -begin - MVC.Free; -end; - -end. -``` - -That's it! You have just created your first DelphiMVCFramework. Now you have to add a controller to respond to the http request. +- From the resultant dialog, leave all the default settings and click `OK` +- If you try to compile the project now, you will get compiler errors because we've to configure the library paths to let the compiler finds the needed source files +- go to `Tools->Options->Language->Delphi->Library` and add in the `Library Path` the following paths: + - C:\DMVC\sources + - C:\DMVC\lib\dmustache + - C:\DMVC\lib\loggerpro + - C:\DMVC\lib\swagdoc\Source +- Run the project (F9) +- Now, a new console application is running serving you first DMVCFramework server. +- Simple, isn't it? +- The wizard generated controller shown some basic setup and some actions. You can start from here to create your application. Use the huge number of sample to understand how each DMVCFramwork feature work. ## Sample Controller + Below a basic sample of a DMVCFramework controller with 2 action ```delphi @@ -929,9 +1164,9 @@ Now you have a performant RESTful server wich respond to the following URLs: - PUT /users/($id) (eg. /users/1, /users/45 etc with the JSON data in the request body) - POST /users (the JSON data must be in the request body) -### Quick Creation of DelphiMVCFramework Server +## How to pack more than one DMVCFramework listener in the same executable -If you dont plan to deploy your DMVCFramework server behind a webserver (apache or IIS) you can also pack more than one listener application server into one single executable. In this case, the process is a bit different and involves the creation of a listener context. However, create a new server is a simple task: +If you don't plan to deploy your DMVCFramework server behind a webserver (apache or IIS) you can also pack more than one listener application server into one single executable. In this case, the process is a bit different and involves the creation of a listener context. However, create a new server is a simple task: ```delphi uses @@ -1014,7 +1249,7 @@ begin end; ``` -#### RQL +## RQL Resource Query Language (RQL) is a query language designed for use in URIs with object style data structures. DMVCFramework supports RQL natively and the included MVCActiveRecord micro-framework, implement a large subset of the RQL specs. @@ -1092,7 +1327,7 @@ count() - Returns the count of the number of records in the query's result set -### Links +## Links Feel free to ask questions on the "Delphi MVC Framework" facebook group (https://www.facebook.com/groups/delphimvcframework). diff --git a/docs/dmvcframework_logofacebook.pdn b/docs/dmvcframework_logofacebook.pdn index 458271a2..fb834b02 100644 Binary files a/docs/dmvcframework_logofacebook.pdn and b/docs/dmvcframework_logofacebook.pdn differ diff --git a/docs/dmvcframework_logofacebook.png b/docs/dmvcframework_logofacebook.png index bb6d3eef..8483a93f 100644 Binary files a/docs/dmvcframework_logofacebook.png and b/docs/dmvcframework_logofacebook.png differ diff --git a/docs/getting_started_10_ide_expert_new_project.png b/docs/getting_started_10_ide_expert_new_project.png new file mode 100644 index 00000000..3e3b370d Binary files /dev/null and b/docs/getting_started_10_ide_expert_new_project.png differ