2016-06-22 17:49:16 +02:00
// ***************************************************************************
//
// Delphi MVC Framework
//
2017-01-05 12:44:34 +01:00
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team
2016-06-22 17:49:16 +02:00
//
// https://github.com/danieleteti/delphimvcframework
//
2017-03-13 20:52:11 +01:00
// Collaborators on this file: Ezequiel Juliano M<> ller (ezequieljuliano@gmail.com)
//
2016-06-22 17:49:16 +02:00
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// *************************************************************************** }
2013-10-30 00:48:23 +01:00
unit MVCFramework;
2017-01-18 21:53:53 +01:00
{$I dmvcframework.inc}
{$IFDEF ANDROID OR IOS}
{$MESSAGE Fatal 'This unit is not compilable on mobile platforms'}
{$ENDIF}
2013-10-30 00:48:23 +01:00
{ $ RTTI EXPLICIT
2014-05-22 01:06:35 +02:00
METHODS( [ vcPublic, vcPublished, vcProtected] )
2013-10-30 00:48:23 +01:00
FIELDS( DefaultFieldRttiVisibility)
PROPERTIES( DefaultPropertyRttiVisibility) }
2013-11-09 14:22:11 +01:00
{$WARNINGS OFF}
2013-10-30 00:48:23 +01:00
interface
uses
2015-12-02 04:14:15 +01:00
System. Classes,
System. SysUtils,
2017-03-13 20:52:11 +01:00
System. TypInfo,
System. IOUtils,
System. SyncObjs,
System. DateUtils,
System. Generics. Collections,
System. Rtti,
2017-04-14 16:43:31 +02:00
// WinApi.Windows,
2015-12-02 04:14:15 +01:00
MVCFramework. Commons,
2017-03-13 20:52:11 +01:00
Data. DB,
2015-12-02 04:14:15 +01:00
MVCFramework. Session,
2017-03-13 20:52:11 +01:00
MVCFramework. DuckTyping,
MVCFramework. Logger,
MVCFramework. ApplicationSession,
MVCFramework. Serializer. Intf,
MVCFramework. Serializer. Commons,
2017-03-01 21:40:57 +01:00
MVCFramework. Serializer. JSON,
2017-03-13 20:52:11 +01:00
{$IFDEF WEBAPACHEHTTP}
Web. ApacheHTTP, // Apache Support since XE6 http://docwiki.embarcadero.com/Libraries/XE6/de/Web.ApacheHTTP
{$ENDIF}
2017-04-14 16:43:31 +02:00
// Delphi XE4 (all update) and XE5 (with no update) dont contains this unit. Look for the bug in QC
// https://quality.embarcadero.com/browse/RSP-17216
{$IFNDEF VER320}
Web. ReqMulti,
{$ENDIF}
2017-03-13 20:52:11 +01:00
Web. HTTPApp,
2017-04-14 16:43:31 +02:00
{$IFNDEF LINUX}
2017-03-13 20:52:11 +01:00
Web. Win. IsapiHTTP,
2017-04-14 16:43:31 +02:00
{$ENDIF}
2017-03-13 20:52:11 +01:00
Web. WebReq,
LoggerPro,
IdGlobal,
IdGlobalProtocols,
2017-05-09 13:37:46 +02:00
IdURI;
2013-10-30 00:48:23 +01:00
type
2017-03-13 20:52:11 +01:00
2016-08-09 13:08:17 +02:00
TSessionData = TDictionary< string , string > ;
2017-04-14 16:43:31 +02:00
TMVCBaseViewEngine = class ;
TMVCViewEngineClass = class of TMVCBaseViewEngine;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
MVCBaseAttribute = class( TCustomAttribute)
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
end ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
MVCHTTPMethodsAttribute = class( MVCBaseAttribute)
2015-12-02 04:14:15 +01:00
private
FMVCHTTPMethods: TMVCHTTPMethods;
function GetMVCHTTPMethodsAsString: string ;
2017-03-13 20:52:11 +01:00
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create( const AMVCHTTPMethods: TMVCHTTPMethods) ;
2015-12-02 04:14:15 +01:00
property MVCHTTPMethods: TMVCHTTPMethods read FMVCHTTPMethods;
property MVCHTTPMethodsAsString: string read GetMVCHTTPMethodsAsString;
end ;
2017-03-13 20:52:11 +01:00
MVCHTTPMethodAttribute = MVCHTTPMethodsAttribute;
2015-12-02 04:14:15 +01:00
MVCStringAttribute = class( MVCBaseAttribute)
private
FValue: string ;
2017-03-13 20:52:11 +01:00
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create( const AValue: string ) ;
2015-12-02 04:14:15 +01:00
property Value: string read FValue;
end ;
MVCConsumesAttribute = class( MVCStringAttribute)
2017-03-13 20:52:11 +01:00
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
MVCProducesAttribute = class( MVCStringAttribute)
private
FEncoding: string ;
protected
{ protected declarations }
public
constructor Create( const AValue: string ) ; overload ;
constructor Create( const AValue: string ; const AEncoding: string ) ; overload ;
property Encoding: string read FEncoding;
2015-12-16 15:57:20 +01:00
end ;
2017-03-13 20:52:11 +01:00
MVCDocAttribute = class( MVCStringAttribute)
2015-12-02 04:14:15 +01:00
private
2017-03-13 20:52:11 +01:00
{ private declarations }
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
{ public declarations }
2015-12-02 04:14:15 +01:00
end ;
MVCPathAttribute = class( MVCBaseAttribute)
private
FPath: string ;
2017-03-13 20:52:11 +01:00
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create( const APath: string ) ; overload ;
2015-12-02 04:14:15 +01:00
property Path: string read FPath;
end ;
TMVCWebRequest = class
private
FWebRequest: TWebRequest;
2017-03-13 20:52:11 +01:00
FSerializers: TDictionary< string , IMVCSerializer> ;
FBody: string ;
2015-12-02 04:14:15 +01:00
FContentType: string ;
FCharset: string ;
2017-03-13 20:52:11 +01:00
FParamsTable: TMVCRequestParamsTable;
procedure DefineContentTypeAndCharset;
function GetHeader( const AName: string ) : string ;
2015-12-02 04:14:15 +01:00
function GetPathInfo: string ;
2017-03-13 20:52:11 +01:00
function GetParams( const AParamName: string ) : string ;
2016-06-22 17:49:16 +02:00
function GetIsAjax: Boolean ;
2015-12-02 04:14:15 +01:00
function GetHTTPMethod: TMVCHTTPMethodType;
function GetHTTPMethodAsString: string ;
2017-03-13 20:52:11 +01:00
function GetParamAsInteger( const AParamName: string ) : Integer ;
function GetParamAsInt64( const AParamName: string ) : Int64 ;
2015-12-02 04:14:15 +01:00
function GetFiles: TAbstractWebRequestFiles;
2017-03-13 20:52:11 +01:00
function GetParamNames: TArray< string > ;
2017-05-08 17:26:19 +02:00
function GetParamsMulti( const AParamName: string ) : TArray< String > ;
2017-03-13 20:52:11 +01:00
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create( const AWebRequest: TWebRequest; const ASerializers: TDictionary< string , IMVCSerializer> ) ;
2015-12-02 04:14:15 +01:00
destructor Destroy; override ;
2017-03-13 20:52:11 +01:00
function ClientIp: string ;
function ClientPrefer( const AMediaType: string ) : Boolean ;
2017-03-20 19:08:01 +01:00
function ClientPreferHTML: Boolean ;
2017-03-13 20:52:11 +01:00
function SegmentParam( const AParamName: string ; out AValue: string ) : Boolean ;
function SegmentParamsCount: Integer ;
2016-06-22 17:49:16 +02:00
function ThereIsRequestBody: Boolean ;
2017-03-13 20:52:11 +01:00
procedure EnsureQueryParamExists( const AName: string ) ;
function QueryStringParam( const AName: string ) : string ;
function QueryStringParamExists( const AName: string ) : Boolean ;
2016-06-23 12:11:01 +02:00
function QueryStringParams: TStrings;
2017-03-13 20:52:11 +01:00
function Accept: string ;
function ContentParam( const AName: string ) : string ;
function Cookie( const AName: string ) : string ;
2015-12-02 04:14:15 +01:00
function Body: string ;
2017-03-13 20:52:11 +01:00
function BodyAs< T: class , constructor > : T;
function BodyAsListOf< T: class , constructor > : TObjectList< T> ;
procedure BodyFor< T: class , constructor > ( const AObject: T) ;
procedure BodyForListOf< T: class , constructor > ( const AObjectList: TObjectList< T> ) ;
property RawWebRequest: TWebRequest read FWebRequest;
property ContentType: string read FContentType;
property Charset: string read FCharset;
property Headers[ const AHeaderName: string ] : string read GetHeader;
property PathInfo: string read GetPathInfo;
property ParamsTable: TMVCRequestParamsTable read FParamsTable write FParamsTable;
property ParamNames: TArray< string > read GetParamNames;
property Params[ const AParamName: string ] : string read GetParams;
2017-05-08 17:26:19 +02:00
property ParamsMulti[ const AParamName: string ] : TArray< String > read GetParamsMulti;
2017-03-13 20:52:11 +01:00
property ParamsAsInteger[ const AParamName: string ] : Integer read GetParamAsInteger;
property ParamsAsInt64[ const AParamName: string ] : Int64 read GetParamAsInt64;
2016-06-22 17:49:16 +02:00
property IsAjax: Boolean read GetIsAjax;
2015-12-02 04:14:15 +01:00
property HTTPMethod: TMVCHTTPMethodType read GetHTTPMethod;
property HTTPMethodAsString: string read GetHTTPMethodAsString;
property Files: TAbstractWebRequestFiles read GetFiles;
end ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
{$IFDEF WEBAPACHEHTTP}
2014-10-03 11:40:57 +02:00
2015-12-02 04:14:15 +01:00
TMVCApacheWebRequest = class( TMVCWebRequest)
2017-03-13 20:52:11 +01:00
private
{ private declarations }
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
{ public declarations }
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
{$ENDIF}
2014-05-14 15:55:41 +02:00
2015-12-02 04:14:15 +01:00
TMVCISAPIWebRequest = class( TMVCWebRequest)
2017-03-13 20:52:11 +01:00
private
{ private declarations }
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
{ public declarations }
2015-12-02 04:14:15 +01:00
end ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
TMVCIndyWebRequest = class( TMVCWebRequest)
private
{ private declarations }
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
{ public declarations }
2015-12-02 04:14:15 +01:00
end ;
2013-10-30 00:48:23 +01:00
2015-12-02 04:14:15 +01:00
TMVCWebResponse = class
private
2017-03-13 20:52:11 +01:00
FWebResponse: TWebResponse;
FFlushOnDestroy: Boolean ;
function GetCustomHeaders: TStrings;
function GetReasonString: string ;
2015-12-02 04:14:15 +01:00
function GetStatusCode: Integer ;
function GetCookies: TCookieCollection;
function GetContentType: string ;
function GetLocation: string ;
2017-03-13 20:52:11 +01:00
function GetContent: string ;
procedure SetReasonString( const AValue: string ) ;
procedure SetStatusCode( const AValue: Integer ) ;
procedure SetContentType( const AValue: string ) ;
procedure SetLocation( const AValue: string ) ;
procedure SetContent( const AValue: string ) ;
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create( const AWebResponse: TWebResponse) ;
2015-12-02 04:14:15 +01:00
destructor Destroy; override ;
2017-03-13 20:52:11 +01:00
2015-12-02 04:14:15 +01:00
procedure Flush;
procedure SendHeaders;
2017-03-13 20:52:11 +01:00
procedure SetCustomHeader( const AName, AValue: string ) ;
procedure SetContentStream( const AStream: TStream; const AContentType: string ) ;
2015-12-02 04:14:15 +01:00
property StatusCode: Integer read GetStatusCode write SetStatusCode;
property ReasonString: string read GetReasonString write SetReasonString;
property ContentType: string read GetContentType write SetContentType;
2017-03-13 20:52:11 +01:00
property CustomHeaders: TStrings read GetCustomHeaders;
property Cookies: TCookieCollection read GetCookies;
2015-12-02 04:14:15 +01:00
property Location: string read GetLocation write SetLocation;
property RawWebResponse: TWebResponse read FWebResponse;
2017-03-13 20:52:11 +01:00
property Content: string read GetContent write SetContent;
2016-06-22 17:49:16 +02:00
property FlushOnDestroy: Boolean read FFlushOnDestroy write FFlushOnDestroy;
2015-12-02 04:14:15 +01:00
end ;
TUser = class
private
FUserName: string ;
2017-03-13 20:52:11 +01:00
FRoles: TList< string > ;
2015-12-02 04:14:15 +01:00
FLoggedSince: TDateTime;
FRealm: string ;
2017-03-13 20:52:11 +01:00
procedure SetLoggedSince( const AValue: TDateTime) ;
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create;
destructor Destroy; override ;
function IsValid: Boolean ;
2015-12-02 04:14:15 +01:00
procedure Clear;
2017-03-13 20:52:11 +01:00
procedure SaveToSession( const AWebSession: TWebSession) ;
function LoadFromSession( const AWebSession: TWebSession) : Boolean ;
property UserName: string read FUserName write FUserName;
2015-12-02 04:14:15 +01:00
property Roles: TList< string > read FRoles;
property LoggedSince: TDateTime read FLoggedSince write SetLoggedSince;
2017-03-13 20:52:11 +01:00
property Realm: string read FRealm write FRealm;
2015-12-02 04:14:15 +01:00
end ;
TWebContext = class
private
FRequest: TMVCWebRequest;
FResponse: TMVCWebResponse;
FConfig: TMVCConfig;
2017-03-13 20:52:11 +01:00
FSerializers: TDictionary< string , IMVCSerializer> ;
2016-06-22 17:49:16 +02:00
FIsSessionStarted: Boolean ;
FSessionMustBeClose: Boolean ;
2017-03-13 20:52:11 +01:00
FLoggedUser: TUser;
FData: TDictionary< string , string > ;
FWebSession: TWebSession;
2016-04-03 22:35:27 +02:00
function GetWebSession: TWebSession;
2015-12-02 04:14:15 +01:00
function GetLoggedUser: TUser;
2017-03-13 20:52:11 +01:00
function GetParamsTable: TMVCRequestParamsTable;
procedure SetParamsTable( const AValue: TMVCRequestParamsTable) ;
protected
procedure Flush; virtual ;
procedure BindToSession( const ASessionId: string ) ;
function SendSessionCookie( const AContext: TWebContext) : string ;
function AddSessionToTheSessionList( const ASessionType, ASessionId: string ; const ASessionTimeout: Integer ) : TWebSession;
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create( const ARequest: TWebRequest; const AResponse: TWebResponse; const AConfig: TMVCConfig; const ASerializers: TDictionary< string , IMVCSerializer> ) ;
2015-12-02 04:14:15 +01:00
destructor Destroy; override ;
2017-03-13 20:52:11 +01:00
procedure SessionStart; virtual ;
procedure SessionStop( const ARaiseExceptionIfExpired: Boolean = True ) ; virtual ;
2016-06-28 13:42:14 +02:00
function SessionStarted: Boolean ;
2017-03-13 20:52:11 +01:00
function SessionId: string ;
function IsSessionStarted: Boolean ;
function SessionMustBeClose: Boolean ;
2015-12-02 04:14:15 +01:00
property LoggedUser: TUser read GetLoggedUser;
property Request: TMVCWebRequest read FRequest;
property Response: TMVCWebResponse read FResponse;
2016-04-03 22:35:27 +02:00
property Session: TWebSession read GetWebSession;
2015-12-02 04:14:15 +01:00
property Config: TMVCConfig read FConfig;
2017-03-13 20:52:11 +01:00
property Data: TDictionary< string , string > read FData;
property ParamsTable: TMVCRequestParamsTable read GetParamsTable write SetParamsTable;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
TMVCEngine = class ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
TMVCBase = class
2015-12-02 04:14:15 +01:00
private
2017-03-13 20:52:11 +01:00
FEngine: TMVCEngine;
2015-12-02 04:14:15 +01:00
FApplicationSession: TWebApplicationSession;
2017-03-13 20:52:11 +01:00
function GetEngine: TMVCEngine;
function GetConfig: TMVCConfig;
function GetApplicationSession: TWebApplicationSession;
procedure SetApplicationSession( const AValue: TWebApplicationSession) ;
procedure SetEngine( const AValue: TMVCEngine) ;
2015-12-02 04:14:15 +01:00
protected
2017-03-13 20:52:11 +01:00
class function GetApplicationFileName: string ; static ;
class function GetApplicationFileNamePath: string ; static ;
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
property Engine: TMVCEngine read GetEngine write SetEngine;
property Config: TMVCConfig read GetConfig;
property ApplicationSession: TWebApplicationSession read GetApplicationSession write SetApplicationSession;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
TMVCStompMessage = class ;
TMVCErrorResponse = class ;
2015-12-02 04:14:15 +01:00
TMVCController = class( TMVCBase)
private
FContext: TWebContext;
FContentCharset: string ;
2017-03-13 20:52:11 +01:00
FResponseStream: TStringBuilder;
2017-03-20 19:08:01 +01:00
FViewModel: TMVCViewDataObject;
FViewDataSets: TObjectDictionary< string , TDataSet> ;
2017-03-13 20:52:11 +01:00
function GetContext: TWebContext;
function GetSession: TWebSession;
2015-12-02 04:14:15 +01:00
function GetContentType: string ;
2017-03-13 20:52:11 +01:00
function GetStatusCode: Integer ;
procedure SetContentType( const AValue: string ) ;
procedure SetStatusCode( const AValue: Integer ) ;
2015-12-02 04:14:15 +01:00
protected const
CLIENTID_KEY = '__clientid' ;
protected
2017-03-13 20:52:11 +01:00
procedure MVCControllerAfterCreate; virtual ;
procedure MVCControllerBeforeDestroy; virtual ;
procedure OnBeforeAction( AContext: TWebContext; const AActionName: string ; var AHandled: Boolean ) ; virtual ;
procedure OnAfterAction( AContext: TWebContext; const AActionName: string ) ; virtual ;
function GetClientId: string ;
2015-12-02 04:14:15 +01:00
function GetCurrentWebModule: TWebModule;
2017-03-20 19:08:01 +01:00
function GetViewModel: TMVCViewDataObject;
function GetViewDataSets: TObjectDictionary< string , TDataSet> ;
function GetRenderedView( const AViewNames: TArray< string > ) : string ; virtual ;
/// <summary>
/// Load mustache view located in TMVCConfigKey.ViewsPath
/// returns the rendered views and generates output using
/// models pushed using Push* methods
/// </summary>
function LoadView( const AViewNames: TArray< string > ) : string ; virtual ;
/// <summary>
/// Load a view fragment in the output render stream. The view fragment is appended to the
/// ResponseStream verbatim. No processing happens.
/// Useful when used with cache.
/// It is equivalent to <code>ResponseStream.Append(AViewFragment);</code>
/// </summary>
procedure LoadViewFragment( const AViewFragment: string ) ;
2016-10-02 17:44:20 +02:00
2017-03-13 20:52:11 +01:00
function ResponseStream: TStringBuilder;
2016-12-05 15:50:00 +01:00
function SessionAs< T: TWebSession> : T;
2017-03-13 20:52:11 +01:00
procedure SendStream( const AStream: TStream; const AOwns: Boolean = True ; const ARewind: Boolean = False ) ; virtual ;
procedure SendFile( const AFileName: string ) ; virtual ;
2016-09-25 16:17:37 +02:00
procedure RenderResponseStream; virtual ;
2017-03-13 20:52:11 +01:00
procedure RaiseSessionExpired; virtual ;
procedure Redirect( const AUrl: string ) ; virtual ;
procedure ResponseStatus( const AStatusCode: Integer ; const AReasonString: string = '' ) ; virtual ;
function Serializer: IMVCSerializer; overload ;
function Serializer( const AContentType: string ) : IMVCSerializer; overload ;
procedure Render( const AContent: string ) ; overload ;
procedure Render( const AObject: TObject) ; overload ;
procedure Render( const AObject: TObject; const AOwns: Boolean ) ; overload ;
procedure Render( const AObject: TObject; const AOwns: Boolean ; const AType: TMVCSerializationType) ; overload ;
procedure Render< T: class > ( const ACollection: TObjectList< T> ) ; overload ;
procedure Render< T: class > ( const ACollection: TObjectList< T> ; const AOwns: Boolean ) ; overload ;
procedure Render< T: class > ( const ACollection: TObjectList< T> ; const AOwns: Boolean ; const AType: TMVCSerializationType) ; overload ;
procedure Render( const ACollection: IMVCList) ; overload ;
procedure Render( const ACollection: IMVCList; const AType: TMVCSerializationType) ; overload ;
2017-04-29 23:56:56 +02:00
procedure Render( const ADataSet: TDataSet) ; overload ;
2017-03-30 17:00:04 +02:00
procedure Render( const ADataSet: TDataSet; const AOwns: boolean ) ; overload ;
2017-04-29 23:56:56 +02:00
procedure Render( const ADataSet: TDataSet; const AOwns: boolean ; const ASerializationType: TMVCDatasetSerializationType) ; overload ;
procedure Render( const ADataSet: TDataSet; const AOwns: boolean ; const AIgnoredFields: TMVCIgnoredList; const ASerializationType: TMVCDatasetSerializationType) ; overload ;
procedure Render( const ADataSet: TDataSet; const AOwns: boolean ; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase; const ASerializationType: TMVCDatasetSerializationType) ; overload ;
2017-03-13 20:52:11 +01:00
procedure Render( const ATextWriter: TTextWriter; const AOwns: Boolean = True ) ; overload ;
procedure Render( const AStream: TStream; const AOwns: Boolean = True ) ; overload ;
procedure Render( const AErrorCode: Integer ; const AErrorMessage: string ; const AErrorClassName: string = '' ) ; overload ;
procedure Render( const AException: Exception; AExceptionItems: TList< string > = nil ; const AOwns: Boolean = True ) ; overload ;
procedure Render( const AError: TMVCErrorResponse; const AOwns: Boolean = True ) ; overload ;
property Context: TWebContext read GetContext write FContext;
property Session: TWebSession read GetSession;
property ContentType: string read GetContentType write SetContentType;
property ContentCharset: string read FContentCharset write FContentCharset;
property StatusCode: Integer read GetStatusCode write SetStatusCode;
2017-03-20 19:08:01 +01:00
property ViewModel: TMVCViewDataObject read GetViewModel;
property ViewDataSets: TObjectDictionary< string , TDataSet> read GetViewDataSets;
2015-12-02 04:14:15 +01:00
public
constructor Create;
destructor Destroy; override ;
2017-03-20 19:08:01 +01:00
procedure PushToView( const AModelName: string ; const AModel: string ) ;
procedure PushObjectToView( const AModelName: string ; const AModel: TObject) ;
procedure PushDataSetToView( const AModelName: string ; const ADataSet: TDataSet) ;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
TMVCControllerClazz = class of TMVCController;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
TMVCControllerCreateAction = reference to function : TMVCController;
2016-02-29 13:48:36 +01:00
2017-03-13 20:52:11 +01:00
TMVCControllerDelegate = class
private
FClazz: TMVCControllerClazz;
FCreateAction: TMVCControllerCreateAction;
protected
{ protected declarations }
2016-02-29 13:48:36 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create( const AClazz: TMVCControllerClazz; const ACreateAction: TMVCControllerCreateAction) ;
2016-02-29 13:48:36 +01:00
2017-03-13 20:52:11 +01:00
property Clazz: TMVCControllerClazz read FClazz;
property CreateAction: TMVCControllerCreateAction read FCreateAction;
end ;
TMVCStaticContents = class( TMVCController)
private
{ private declarations }
protected
{ protected declarations }
public
class procedure SendFile( const AFileName, AMediaType: string ; AContext: TWebContext) ;
class function IsStaticFile( const AViewPath, AWebRequestPath: string ; out ARealFileName: string ) : Boolean ;
class function IsScriptableFile( const AStaticFileName: string ; const AConfig: TMVCConfig) : Boolean ;
2016-02-29 13:48:36 +01:00
end ;
2016-02-23 23:22:44 +01:00
/// <summary>
/// Basis Interface for DMVC Middleware.
/// </summary>
2015-12-02 04:14:15 +01:00
IMVCMiddleware = interface
[ '{3278183A-124A-4214-AB4E-94CA4C22450D}' ]
2016-02-23 23:22:44 +01:00
/// <summary>
/// Procedure is called before the MVCEngine routes the request to a specific controller/method.
/// </summary>
2017-03-13 20:52:11 +01:00
/// <param name="AContext">Webcontext which contains the complete request and response of the actual call.</param>
/// <param name="AHandled">If set to True the Request would finished. Response must be set by the implementor. Default value is False.</param>
2017-03-23 18:51:25 +01:00
procedure OnBeforeRouting(
AContext: TWebContext;
var AHandled: Boolean
) ;
2016-02-23 23:22:44 +01:00
/// <summary>
/// Procedure is called before the specific controller method is called.
/// </summary>
2017-03-13 20:52:11 +01:00
/// <param name="AContext">Webcontext which contains the complete request and response of the actual call.</param>
2016-02-23 23:22:44 +01:00
/// <param name="AControllerQualifiedClassName">Qualified classname of the matching controller.</param>
2017-03-13 20:52:11 +01:00
/// <param name="AActionName">Method name of the matching controller method.</param>
/// <param name="AHandled">If set to True the Request would finished. Response must be set by the implementor. Default value is False.</param>
2017-03-23 18:51:25 +01:00
procedure OnBeforeControllerAction(
AContext: TWebContext;
const AControllerQualifiedClassName: string ;
const AActionName: string ;
var AHandled: Boolean
) ;
2016-02-23 23:22:44 +01:00
/// <summary>
/// Procedure is called after the specific controller method was called.
/// It is still possible to cancel or to completly modifiy the request.
/// </summary>
2017-03-13 20:52:11 +01:00
/// <param name="AContext">Webcontext which contains the complete request and response of the actual call.</param>
/// <param name="AActionName">Method name of the matching controller method.</param>
/// <param name="AHandled">If set to True the Request would finished. Response must be set by the implementor. Default value is False.</param>
2017-03-23 18:51:25 +01:00
procedure OnAfterControllerAction(
AContext: TWebContext;
const AActionName: string ;
const AHandled: Boolean
) ;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
TMVCEngine = class
private const
ALLOWED_TYPED_ACTION_PARAMETERS_TYPES = 'Integer, Int64, Single, Double, Extended, Boolean, TDate, TTime, TDateTime and String' ;
2015-12-02 04:14:15 +01:00
private
2017-04-14 16:43:31 +02:00
FViewEngineClass: TMVCViewEngineClass;
2015-12-02 04:14:15 +01:00
FWebModule: TWebModule;
2017-03-13 20:52:11 +01:00
FConfig: TMVCConfig;
2017-04-14 16:43:31 +02:00
FSerializers: TDictionary< string , IMVCSerializer> ;
2017-03-13 20:52:11 +01:00
FMiddlewares: TList< IMVCMiddleware> ;
FControllers: TObjectList< TMVCControllerDelegate> ;
FMediaTypes: TDictionary< string , string > ;
FApplicationSession: TWebApplicationSession;
FSavedOnBeforeDispatch: THTTPMethodEvent;
function IsStaticFileRequest( const ARequest: TWebRequest; out AFileName: string ) : Boolean ;
function SendStaticFileIfPresent( const AContext: TWebContext; const AFileName: String ) : Boolean ;
procedure FillActualParamsForAction(
const AContext: TWebContext;
const AActionFormalParams: TArray< TRttiParameter> ;
const AActionName: string ;
var AActualParams: TArray< TValue> ) ;
2017-04-14 16:43:31 +02:00
procedure RegisterDefaultsSerializers;
function GetViewEngineClass: TMVCViewEngineClass;
2015-12-02 04:14:15 +01:00
protected
procedure ConfigDefaultValues; virtual ;
procedure LoadSystemControllers; virtual ;
2017-03-13 20:52:11 +01:00
procedure FixUpWebModule;
procedure ExecuteBeforeRoutingMiddleware( const AContext: TWebContext; var AHandled: Boolean ) ;
procedure ExecuteBeforeControllerActionMiddleware(
const AContext: TWebContext;
const AControllerQualifiedClassName: string ;
const AActionName: string ;
var AHandled: Boolean ) ;
procedure ExecuteAfterControllerActionMiddleware( const AContext: TWebContext; const AActionName: string ; const AHandled: Boolean ) ;
procedure DefineDefaultReponseHeaders( const AContext: TWebContext) ;
procedure OnBeforeDispatch( ASender: TObject; ARequest: TWebRequest; AResponse: TWebResponse; var AHandled: Boolean ) ; virtual ;
procedure ResponseErrorPage( const AException: Exception; const ARequest: TWebRequest; const AResponse: TWebResponse) ; virtual ;
function ExecuteAction( const ASender: TObject; const ARequest: TWebRequest; const AResponse: TWebResponse) : Boolean ; virtual ;
public
class function GetCurrentSession( const ASessionTimeout: Integer ; const ASessionId: string ; const ARaiseExceptionIfExpired: Boolean = True ) : TWebSession; static ;
class function ExtractSessionIdFromWebRequest( const AWebRequest: TWebRequest) : string ; static ;
class function SendSessionCookie( const AContext: TWebContext) : string ; overload ; static ;
class function SendSessionCookie( const AContext: TWebContext; const ASessionId: string ) : string ; overload ; static ;
class procedure ClearSessionCookiesAlreadySet( const ACookies: TCookieCollection) ; static ;
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
constructor Create( const AWebModule: TWebModule; const AConfigAction: TProc< TMVCConfig> = nil ; const ACustomLogger: ILogWriter = nil ) ; reintroduce ;
2015-12-02 04:14:15 +01:00
destructor Destroy; override ;
2017-03-13 20:52:11 +01:00
function GetSessionBySessionId( const ASessionId: string ) : TWebSession;
2017-03-02 19:29:43 +01:00
function AddSerializer( const AContentType: string ; const ASerializer: IMVCSerializer) : TMVCEngine;
2017-03-13 20:52:11 +01:00
function AddMiddleware( const AMiddleware: IMVCMiddleware) : TMVCEngine;
function AddController( const AControllerClazz: TMVCControllerClazz) : TMVCEngine; overload ;
function AddController( const AControllerClazz: TMVCControllerClazz; const ACreateAction: TMVCControllerCreateAction) : TMVCEngine; overload ;
2017-04-14 16:43:31 +02:00
function SetViewEngine( const AViewEngineClass: TMVCViewEngineClass) : TMVCEngine;
2017-03-13 20:52:11 +01:00
procedure HTTP404( const AContext: TWebContext) ;
procedure HTTP500( const AContext: TWebContext; const AReasonString: string = '' ) ;
2017-04-14 16:43:31 +02:00
property ViewEngineClass: TMVCViewEngineClass read GetViewEngineClass;
2017-03-13 20:52:11 +01:00
property WebModule: TWebModule read FWebModule;
property Config: TMVCConfig read FConfig;
2017-04-14 16:43:31 +02:00
property Serializers: TDictionary< string , IMVCSerializer> read FSerializers;
2017-03-13 20:52:11 +01:00
property Middlewares: TList< IMVCMiddleware> read FMiddlewares;
property Controllers: TObjectList< TMVCControllerDelegate> read FControllers;
property ApplicationSession: TWebApplicationSession read FApplicationSession write FApplicationSession;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
TMVCStompMessage = class
private
FSmTimestamp: TDateTime;
FSmQueue: string ;
FSmUsername: string ;
FSmTopic: string ;
FSmMessage: string ;
protected
{ protected declarations }
2015-12-02 04:14:15 +01:00
public
2017-03-13 20:52:11 +01:00
[ MVCNameAs( 'message' ) ]
property SmMessage: string read FSmMessage write FSmMessage;
[ MVCNameAs( '_queue' ) ]
property SmQueue: string read FSmQueue write FSmQueue;
[ MVCNameAs( '_topic' ) ]
property SmTopic: string read FSmTopic write FSmTopic;
[ MVCNameAs( '_username' ) ]
property SmUsername: string read FSmUsername write FSmUsername;
[ MVCNameAs( '_timestamp' ) ]
property SmTimestamp: TDateTime read FSmTimestamp write FSmTimestamp;
2015-12-02 04:14:15 +01:00
end ;
2013-10-30 00:48:23 +01:00
2017-03-29 14:49:35 +02:00
[ MVCNameCase( ncLowerCase) ]
2017-03-13 20:52:11 +01:00
TMVCErrorResponseItem = class
private
FMessage: string ;
protected
{ protected declarations }
public
property Message : string read FMessage write FMessage;
end ;
2017-03-29 14:49:35 +02:00
[ MVCNameCase( ncLowerCase) ]
2017-03-13 20:52:11 +01:00
TMVCErrorResponse = class
private
FStatusCode: Integer ;
FReasonString: string ;
FMessage: string ;
FClassname: string ;
FItems: TObjectList< TMVCErrorResponseItem> ;
protected
{ protected declarations }
public
2017-05-09 23:13:51 +02:00
constructor Create; overload ;
constructor Create( AStatusCode: Integer ; AReasonString: String ; AMessage: String ) ; overload ;
2017-03-13 20:52:11 +01:00
destructor Destroy; override ;
property StatusCode: Integer read FStatusCode write FStatusCode;
property ReasonString: string read FReasonString write fReasonString;
property Message : string read FMessage write FMessage;
property Classname: string read FClassname write FClassname;
[ MVCListOf( TMVCErrorResponseItem) ]
property Items: TObjectList< TMVCErrorResponseItem> read FItems;
2015-12-02 04:14:15 +01:00
end ;
2014-03-10 17:39:29 +01:00
2017-04-14 16:43:31 +02:00
TMVCBaseViewEngine = class( TMVCBase)
private
FViewName: string ;
FWebContext: TWebContext;
FViewModel: TMVCViewDataObject;
FViewDataSets: TObjectDictionary< string , TDataSet> ;
FContentType: string ;
FOutput: string ;
protected
function GetRealFileName( const AViewName: string ) : string ; virtual ;
function IsCompiledVersionUpToDate( const AFileName, ACompiledFileName: string ) : Boolean ; virtual ; abstract ;
procedure SetOutput( const AOutput: String ) ;
public
constructor Create(
const AViewName: string ;
const AEngine: TMVCEngine;
const AWebContext: TWebContext;
const AViewModel: TMVCViewDataObject;
const AViewDataSets: TObjectDictionary< string , TDataSet> ;
const AContentType: string ) ; virtual ;
destructor Destroy; override ;
procedure Execute; virtual ; abstract ;
property ViewName: string read FViewName;
property WebContext: TWebContext read FWebContext;
property ViewModel: TMVCViewDataObject read FViewModel;
property ViewDataSets: TObjectDictionary< string , TDataSet> read FViewDataSets;
property ContentType: string read FContentType;
property Output: string read FOutput;
end ;
2016-06-22 17:49:16 +02:00
function IsShuttingDown: Boolean ;
2013-10-30 00:48:23 +01:00
procedure EnterInShutdownState;
implementation
uses
2015-12-02 04:14:15 +01:00
MVCFramework. Router,
2017-05-09 13:37:46 +02:00
MVCFramework. SysControllers;
2013-10-30 01:09:09 +01:00
2013-10-30 00:48:23 +01:00
var
2015-12-02 04:14:15 +01:00
_IsShuttingDown: Int64 = 0 ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
function IsShuttingDown: Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = TInterlocked. Read( _IsShuttingDown) = 1
2016-02-29 13:48:36 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure EnterInShutdownState;
2016-02-29 13:48:36 +01:00
begin
2017-03-13 20:52:11 +01:00
TInterlocked. Add( _IsShuttingDown, 1 ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ MVCHTTPMethodsAttribute }
2014-03-31 11:25:16 +02:00
2017-03-13 20:52:11 +01:00
constructor MVCHTTPMethodsAttribute. Create( const AMVCHTTPMethods: TMVCHTTPMethods) ;
2017-03-01 21:40:57 +01:00
begin
2017-03-13 20:52:11 +01:00
inherited Create;
FMVCHTTPMethods : = AMVCHTTPMethods;
2017-03-01 21:40:57 +01:00
end ;
2017-03-13 20:52:11 +01:00
function MVCHTTPMethodsAttribute. GetMVCHTTPMethodsAsString: string ;
2015-04-01 17:01:23 +02:00
var
2017-03-13 20:52:11 +01:00
I: TMVCHTTPMethodType;
2015-04-01 17:01:23 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = '' ;
2016-12-05 15:50:00 +01:00
2017-03-13 20:52:11 +01:00
for I : = Low( TMVCHTTPMethodType) to High( TMVCHTTPMethodType) do
if I in FMVCHTTPMethods then
Result : = Result + ',' + GetEnumName( TypeInfo( TMVCHTTPMethodType) , Ord( I) ) ;
2015-04-01 17:01:23 +02:00
2017-03-13 20:52:11 +01:00
if Result < > EmptyStr then
Result : = Result . Remove( 0 , 1 )
else
Result : = 'any' ;
2016-09-16 23:54:54 +02:00
end ;
2017-03-13 20:52:11 +01:00
{ MVCStringAttribute }
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
constructor MVCStringAttribute. Create( const AValue: string ) ;
begin
inherited Create;
FValue : = AValue;
end ;
2016-04-20 11:02:27 +02:00
2017-03-13 20:52:11 +01:00
{ MVCProducesAttribute }
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
constructor MVCProducesAttribute. Create( const AValue, AEncoding: string ) ;
begin
Create( AValue) ;
FEncoding : = AEncoding;
2015-10-18 16:35:50 +02:00
end ;
2017-03-13 20:52:11 +01:00
constructor MVCProducesAttribute. Create( const AValue: string ) ;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
inherited Create( AValue) ;
FEncoding : = TMVCCharset. UTF_8;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ MVCPathAttribute }
constructor MVCPathAttribute. Create( const APath: string ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
inherited Create;
FPath : = APath;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ TMVCWebRequest }
function TMVCWebRequest. Accept: string ;
2016-02-28 19:06:05 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. Accept;
2016-02-28 19:06:05 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. Body: string ;
2013-10-30 00:48:23 +01:00
var
2017-03-13 20:52:11 +01:00
Encoding: TEncoding;
Buffer: TArray< Byte > ;
I: Integer ;
2016-04-20 11:02:27 +02:00
2017-03-13 20:52:11 +01:00
{$IFNDEF BERLINORBETTER}
BufferOut: TArray< Byte > ;
2016-12-07 12:34:51 +01:00
2017-03-13 20:52:11 +01:00
{$ENDIF}
begin
{ TODO -oEzequiel -cRefactoring : Refactoring the method TMVCWebRequest.Body }
if ( FBody = EmptyStr) then
2016-06-22 17:49:16 +02:00
begin
2017-03-13 20:52:11 +01:00
Encoding : = nil ;
try
2016-06-22 17:49:16 +02:00
2017-03-13 20:52:11 +01:00
{$IFDEF BERLINORBETTER}
2016-06-22 17:49:16 +02:00
2017-03-13 20:52:11 +01:00
if ( FCharset = EmptyStr) then
begin
SetLength( Buffer, 1 0 ) ;
for I : = 0 to 9 do
Buffer[ I] : = FWebRequest. RawContent[ I] ;
TEncoding. GetBufferEncoding( Buffer, Encoding, TEncoding. Default ) ;
SetLength( Buffer, 0 ) ;
end
else
Encoding : = TEncoding. GetEncoding( FCharset) ;
FBody : = Encoding. GetString( FWebRequest. RawContent) ;
2016-06-22 17:49:16 +02:00
2017-03-13 20:52:11 +01:00
{$ELSE}
2016-06-22 17:49:16 +02:00
2017-03-13 20:52:11 +01:00
SetLength( Buffer, FWebRequest. ContentLength) ;
FWebRequest. ReadClient( Buffer[ 0 ] , FWebRequest. ContentLength) ;
if ( FCharset = EmptyStr) then
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
SetLength( BufferOut, 1 0 ) ;
for I : = 0 to 9 do
2016-09-05 15:34:17 +02:00
begin
2017-03-13 20:52:11 +01:00
BufferOut[ I] : = Buffer[ I] ;
2016-09-05 15:34:17 +02:00
end ;
2017-03-13 20:52:11 +01:00
TEncoding. GetBufferEncoding( BufferOut, Encoding, TEncoding. Default ) ;
SetLength( BufferOut, 0 ) ;
2015-12-02 04:14:15 +01:00
end
else
2017-03-13 20:52:11 +01:00
Encoding : = TEncoding. GetEncoding( FCharset) ;
FBody : = Encoding. GetString( Buffer) ;
2017-01-05 12:44:34 +01:00
2017-03-13 20:52:11 +01:00
{$ENDIF}
2015-12-02 04:14:15 +01:00
finally
2017-03-13 20:52:11 +01:00
if Assigned( Encoding) then
Encoding. Free;
2015-12-02 04:14:15 +01:00
end ;
end ;
2017-03-13 20:52:11 +01:00
Result : = FBody;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. BodyAs< T> : T;
2014-03-31 11:25:16 +02:00
var
2017-03-13 20:52:11 +01:00
Obj: TObject;
2014-03-31 11:25:16 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = nil ;
if FSerializers. ContainsKey( ContentType) then
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
Obj : = TMVCSerializerHelpful. CreateObject( TClass( T) . QualifiedClassName) ;
FSerializers. Items[ ContentType] . DeserializeObject( Body, Obj) ;
Result : = Obj as T;
end
else
raise EMVCException. CreateFmt( 'Body ContentType %s not supported' , [ ContentType] ) ;
2014-03-31 11:25:16 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. BodyAsListOf< T> : TObjectList< T> ;
2015-04-01 17:01:23 +02:00
var
2017-03-13 20:52:11 +01:00
List: TObjectList< T> ;
2015-04-01 17:01:23 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = nil ;
if FSerializers. ContainsKey( ContentType) then
begin
List : = TObjectList< T> . Create;
FSerializers. Items[ ContentType] . DeserializeCollection( Body, List, T) ;
Result : = List;
end
else
raise EMVCException. CreateFmt( 'Body ContentType %s not supported' , [ ContentType] ) ;
2015-04-01 17:01:23 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCWebRequest. BodyFor< T> ( const AObject: T) ;
2014-03-31 11:25:16 +02:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( AObject) then
if FSerializers. ContainsKey( ContentType) then
FSerializers. Items[ ContentType] . DeserializeObject( Body, AObject)
else
raise EMVCException. CreateFmt( 'Body ContentType %s not supported' , [ ContentType] ) ;
2014-03-31 11:25:16 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCWebRequest. BodyForListOf< T> ( const AObjectList: TObjectList< T> ) ;
2015-10-18 16:35:50 +02:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( AObjectList) then
if FSerializers. ContainsKey( ContentType) then
FSerializers. Items[ ContentType] . DeserializeCollection( Body, AObjectList, T)
else
raise EMVCException. CreateFmt( 'Body ContentType %s not supported' , [ ContentType] ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. ClientIp: string ;
var
S: string ;
2017-03-01 21:40:57 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = EmptyStr;
2017-03-01 21:40:57 +01:00
2017-03-13 20:52:11 +01:00
if FWebRequest. GetFieldByName( 'HTTP_CLIENT_IP' ) < > EmptyStr then
Exit( FWebRequest. GetFieldByName( 'HTTP_CLIENT_IP' ) ) ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
for S in string( FWebRequest. GetFieldByName( 'HTTP_X_FORWARDED_FOR' ) ) . Split( [ ',' ] ) do
if not S. Trim. IsEmpty then
Exit( S. Trim) ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
if FWebRequest. GetFieldByName( 'HTTP_X_FORWARDED' ) < > EmptyStr then
Exit( FWebRequest. GetFieldByName( 'HTTP_X_FORWARDED' ) ) ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
if FWebRequest. GetFieldByName( 'HTTP_X_CLUSTER_CLIENT_IP' ) < > EmptyStr then
Exit( FWebRequest. GetFieldByName( 'HTTP_X_CLUSTER_CLIENT_IP' ) ) ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
if FWebRequest. GetFieldByName( 'HTTP_FORWARDED_FOR' ) < > EmptyStr then
Exit( FWebRequest. GetFieldByName( 'HTTP_FORWARDED_FOR' ) ) ;
2015-10-18 16:35:50 +02:00
2017-03-13 20:52:11 +01:00
if FWebRequest. GetFieldByName( 'HTTP_FORWARDED' ) < > EmptyStr then
Exit( FWebRequest. GetFieldByName( 'HTTP_FORWARDED' ) ) ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
if FWebRequest. GetFieldByName( 'REMOTE_ADDR' ) < > EmptyStr then
Exit( FWebRequest. GetFieldByName( 'REMOTE_ADDR' ) ) ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
if FWebRequest. RemoteIP < > EmptyStr then
Exit( FWebRequest. RemoteIP) ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
if FWebRequest. RemoteAddr < > EmptyStr then
Exit( FWebRequest. RemoteAddr) ;
2015-10-18 16:35:50 +02:00
2017-03-13 20:52:11 +01:00
if FWebRequest. RemoteHost < > EmptyStr then
Exit( FWebRequest. RemoteHost) ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
if FWebRequest. RemoteAddr < > EmptyStr then
Exit( FWebRequest. RemoteAddr) ;
2015-10-18 16:35:50 +02:00
2017-03-13 20:52:11 +01:00
if FWebRequest. RemoteIP < > EmptyStr then
Exit( FWebRequest. RemoteIP) ;
if FWebRequest. RemoteHost < > EmptyStr then
Exit( FWebRequest. RemoteHost) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. ClientPrefer( const AMediaType: string ) : Boolean ;
2015-04-01 17:01:23 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = AnsiPos( AMediaType, LowerCase( RawWebRequest. Accept) ) = 1 ;
2015-04-01 17:01:23 +02:00
end ;
2017-03-20 19:08:01 +01:00
function TMVCWebRequest. ClientPreferHTML: Boolean ;
begin
Result : = ClientPrefer( TMVCMediaType. TEXT_HTML) ;
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. ContentParam( const AName: string ) : string ;
2015-04-01 17:01:23 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. ContentFields. Values[ AName] ;
2015-04-01 17:01:23 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. Cookie( const AName: string ) : string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. CookieFields. Values[ AName] ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
constructor TMVCWebRequest. Create( const AWebRequest: TWebRequest; const ASerializers: TDictionary< string , IMVCSerializer> ) ;
2013-10-30 00:48:23 +01:00
begin
2015-12-02 04:14:15 +01:00
inherited Create;
2017-03-13 20:52:11 +01:00
FBody : = EmptyStr;
FContentType : = TMVCConstants. DEFAULT_CONTENT_TYPE;
FCharset : = TMVCConstants. DEFAULT_CONTENT_CHARSET;
FWebRequest : = AWebRequest;
FSerializers : = ASerializers;
FParamsTable : = nil ;
DefineContentTypeAndCharset;
end ;
2014-05-14 15:55:41 +02:00
2017-03-13 20:52:11 +01:00
procedure TMVCWebRequest. DefineContentTypeAndCharset;
var
RequestContentType: string ;
ContentTypeValues: TArray< string > ;
begin
RequestContentType : = FWebRequest. GetFieldByName( 'Content-Type' ) ;
if not RequestContentType. IsEmpty then
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
ContentTypeValues : = RequestContentType. Split( [ ';' ] ) ;
FContentType : = Trim( ContentTypeValues[ 0 ] ) ;
if Length( ContentTypeValues) > 1 then
if ContentTypeValues[ 1 ] . Trim. StartsWith( 'charset' , True ) then
FCharset : = ContentTypeValues[ 1 ] . Trim. Split( [ '=' ] ) [ 1 ] . Trim;
2015-12-02 04:14:15 +01:00
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
destructor TMVCWebRequest. Destroy;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
inherited Destroy;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCWebRequest. EnsureQueryParamExists( const AName: string ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if GetParams( AName) . IsEmpty then
raise EMVCException. CreateFmt( 'Parameter "%s" required' , [ AName] ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetFiles: TAbstractWebRequestFiles;
2014-03-31 11:25:16 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. Files;
2014-03-31 11:25:16 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetHeader( const AName: string ) : string ;
2015-04-01 17:01:23 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. GetFieldByName( AName) ;
2015-04-01 17:01:23 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetHTTPMethod: TMVCHTTPMethodType;
2016-04-03 22:35:27 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = TMVCRouter. StringMethodToHTTPMetod( FWebRequest. Method) ;
2016-04-03 22:35:27 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetHTTPMethodAsString: string ;
2016-04-03 22:35:27 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. Method;
2016-04-03 22:35:27 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetIsAjax: Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = LowerCase( FWebRequest. GetFieldByName( 'X-Requested-With' ) ) = 'xmlhttprequest' ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetParamAsInt64( const AParamName: string ) : Int64 ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = StrToInt64( GetParams( AParamName) ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetParamAsInteger( const AParamName: string ) : Integer ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = StrToInt( GetParams( AParamName) ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetParamNames: TArray< string > ;
var
I: Integer ;
Names: TList< string > ;
N: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Names : = TList< string > . Create;
2015-12-02 04:14:15 +01:00
try
2017-03-13 20:52:11 +01:00
if Assigned( FParamsTable) and ( Length( FParamsTable. Keys. ToArray) > 0 ) then
for N in FParamsTable. Keys. ToArray do
Names. Add( N) ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
if ( FWebRequest. QueryFields. Count > 0 ) then
for I : = 0 to FWebRequest. QueryFields. Count - 1 do
Names. Add( FWebRequest. QueryFields. Names[ I] ) ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
if ( FWebRequest. ContentFields. Count > 0 ) then
for I : = 0 to FWebRequest. ContentFields. Count - 1 do
Names. Add( FWebRequest. ContentFields. Names[ I] ) ;
if ( FWebRequest. CookieFields. Count > 0 ) then
for I : = 0 to FWebRequest. CookieFields. Count - 1 do
Names. Add( FWebRequest. CookieFields. Names[ I] ) ;
Result : = Names. ToArray;
finally
Names. Free;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetParams( const AParamName: string ) : string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if ( not Assigned( FParamsTable) ) or ( not FParamsTable. TryGetValue( AParamName, Result ) ) then
begin
2017-05-08 17:26:19 +02:00
Result : = FWebRequest. ContentFields. Values[ AParamName] ;
2017-03-13 20:52:11 +01:00
if Result . IsEmpty then
2017-05-08 17:26:19 +02:00
Result : = FWebRequest. QueryFields. Values[ AParamName] ;
2017-03-13 20:52:11 +01:00
if Result . IsEmpty then
Result : = FWebRequest. CookieFields. Values[ AParamName] ;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-05-08 17:26:19 +02:00
function TMVCWebRequest. GetParamsMulti(
const AParamName: string ) : TArray< String > ;
var
lList: TList< String > ;
procedure AddParamsToList( const AStrings: TStrings; const AList: TList< String > ) ;
var
I: Integer ;
begin
for I : = 0 to AStrings. Count - 1 do
if SameText( AStrings. Names[ i] , AParamName) then
AList. Add( AStrings. ValueFromIndex[ i] ) ;
end ;
begin
lList : = TList< String > . Create;
try
AddParamsToList( FWebRequest. ContentFields, lList) ;
AddParamsToList( FWebRequest. QueryFields, lList) ;
AddParamsToList( FWebRequest. CookieFields, lList) ;
Result : = lList. ToArray;
finally
lList. Free;
end ;
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. GetPathInfo: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. PathInfo;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. QueryStringParam( const AName: string ) : string ;
2014-03-25 12:41:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. QueryFields. Values[ AName] ;
2014-03-25 12:41:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. QueryStringParamExists( const AName: string ) : Boolean ;
2015-08-27 11:13:40 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = QueryStringParam( AName) < > EmptyStr;
2015-08-27 11:13:40 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. QueryStringParams: TStrings;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebRequest. QueryFields;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. SegmentParam( const AParamName: string ; out AValue: string ) : Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = False ;
if Assigned( FParamsTable) then
Result : = FParamsTable. TryGetValue( AParamName, AValue) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. SegmentParamsCount: Integer ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = 0 ;
if Assigned( FParamsTable) then
Result : = FParamsTable. Count;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebRequest. ThereIsRequestBody: Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = ( FWebRequest. Content < > EmptyStr) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ TMVCWebResponse }
constructor TMVCWebResponse. Create( const AWebResponse: TWebResponse) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
inherited Create;
FWebResponse : = AWebResponse;
FFlushOnDestroy : = True ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
destructor TMVCWebResponse. Destroy;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if FFlushOnDestroy then
Flush;
inherited Destroy;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. Flush;
2014-03-25 12:41:23 +01:00
begin
2017-03-13 20:52:11 +01:00
try
FWebResponse. SendResponse;
except
{ TODO -oEzequiel -cException : Check why this exception is being eaten }
end ;
2014-03-25 12:41:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebResponse. GetContent: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebResponse. Content;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebResponse. GetContentType: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebResponse. ContentType;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebResponse. GetCookies: TCookieCollection;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebResponse. Cookies;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebResponse. GetCustomHeaders: TStrings;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebResponse. CustomHeaders;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebResponse. GetLocation: string ;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = CustomHeaders. Values[ 'location' ] ;
2013-11-18 00:16:59 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebResponse. GetReasonString: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebResponse. ReasonString;
2013-11-09 14:22:11 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCWebResponse. GetStatusCode: Integer ;
2013-11-09 14:22:11 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FWebResponse. StatusCode;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. SendHeaders;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
FWebResponse. SendResponse;
2013-11-18 00:16:59 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. SetContent( const AValue: string ) ;
2015-12-16 15:57:20 +01:00
begin
2017-03-13 20:52:11 +01:00
FWebResponse. Content : = AValue;
end ;
2015-12-16 15:57:20 +01:00
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. SetContentStream( const AStream: TStream; const AContentType: string ) ;
begin
FWebResponse. ContentType : = AContentType;
FWebResponse. ContentStream : = AStream;
end ;
2015-12-16 15:57:20 +01:00
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. SetContentType( const AValue: string ) ;
begin
FWebResponse. ContentType : = AValue;
end ;
2015-12-16 15:57:20 +01:00
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. SetCustomHeader( const AName, AValue: string ) ;
begin
FWebResponse. SetCustomHeader( AName, AValue) ;
end ;
2015-12-16 15:57:20 +01:00
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. SetLocation( const AValue: string ) ;
begin
CustomHeaders. Values[ 'location' ] : = AValue;
end ;
2015-12-16 15:57:20 +01:00
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. SetReasonString( const AValue: string ) ;
begin
FWebResponse. ReasonString : = AValue;
end ;
2015-12-16 15:57:20 +01:00
2017-03-13 20:52:11 +01:00
procedure TMVCWebResponse. SetStatusCode( const AValue: Integer ) ;
begin
FWebResponse. StatusCode : = AValue;
end ;
2015-12-16 15:57:20 +01:00
2017-03-13 20:52:11 +01:00
{ TUser }
2015-12-16 15:57:20 +01:00
2017-03-13 20:52:11 +01:00
procedure TUser. Clear;
begin
FUserName : = EmptyStr;
FLoggedSince : = 0 ;
FRealm : = EmptyStr;
FRoles. Clear;
2015-12-16 15:57:20 +01:00
end ;
2017-03-13 20:52:11 +01:00
constructor TUser. Create;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
inherited Create;
FRoles : = TList< string > . Create;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
destructor TUser. Destroy;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
FRoles. Free;
inherited Destroy;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TUser. IsValid: Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = ( not UserName. IsEmpty) and ( LoggedSince > 0 ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TUser. LoadFromSession( const AWebSession: TWebSession) : Boolean ;
2013-11-18 00:16:59 +01:00
var
2017-03-13 20:52:11 +01:00
SerObj: string ;
Pieces: TArray< string > ;
I: Integer ;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
if not Assigned( AWebSession) then
Exit( False ) ;
SerObj : = AWebSession[ TMVCConstants. CURRENT_USER_SESSION_KEY] ;
Result : = not SerObj. IsEmpty;
if Result then
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
Clear;
Pieces : = SerObj. Split( [ '$$' ] , TStringSplitOptions. None) ;
UserName : = Pieces[ 0 ] ;
LoggedSince : = ISOTimeStampToDateTime( Pieces[ 1 ] ) ;
Realm : = Pieces[ 2 ] ;
Roles. Clear;
for I : = 2 to Length( Pieces) - 1 do
Roles. Add( Pieces[ I] ) ;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
end ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
procedure TUser. SaveToSession( const AWebSession: TWebSession) ;
var
LRoles: string ;
begin
if ( FRoles. Count > 0 ) then
LRoles : = string . Join( '$$' , FRoles. ToArray)
else
LRoles : = '' ;
AWebSession[ TMVCConstants. CURRENT_USER_SESSION_KEY] : = FUserName + '$$' + DateTimeToISOTimeStamp( FLoggedSince) + '$$' + FRealm + '$$' + LRoles;
2013-11-18 00:16:59 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TUser. SetLoggedSince( const AValue: TDateTime) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if ( FLoggedSince = 0 ) then
FLoggedSince : = AValue
else
raise EMVCException. Create( 'TUser.LoggedSince already set.' ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ TWebContext }
function TWebContext. AddSessionToTheSessionList( const ASessionType, ASessionId: string ; const ASessionTimeout: Integer ) : TWebSession;
var
Session: TWebSession;
begin
if ( Trim( ASessionType) = EmptyStr) then
raise EMVCException. Create( 'Empty Session Type' ) ;
2017-03-23 18:51:25 +01:00
TMonitor. Enter( GlobalSessionList) ;
2017-03-13 20:52:11 +01:00
try
Session : = TMVCSessionFactory. GetInstance. CreateNewByType( ASessionType, ASessionId, ASessionTimeout) ;
2017-03-23 18:51:25 +01:00
GlobalSessionList. Add( ASessionId, Session) ;
2017-03-13 20:52:11 +01:00
Result : = Session;
Session. MarkAsUsed;
finally
2017-03-23 18:51:25 +01:00
TMonitor. Exit( GlobalSessionList) ;
2017-03-13 20:52:11 +01:00
end ;
end ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
procedure TWebContext. BindToSession( const ASessionId: string ) ;
2013-10-30 00:48:23 +01:00
begin
2015-12-02 04:14:15 +01:00
if not Assigned( FWebSession) then
begin
2017-03-13 20:52:11 +01:00
FWebSession : = TMVCEngine. GetCurrentSession( StrToInt64( FConfig[ TMVCConfigKey. SessionTimeout] ) , ASessionId, False ) ;
2015-12-02 04:14:15 +01:00
if not Assigned( FWebSession) then
raise EMVCException. Create( 'Invalid SessionID' ) ;
FWebSession. MarkAsUsed;
2017-03-13 20:52:11 +01:00
TMVCEngine. SendSessionCookie( Self, ASessionId) ;
2015-12-02 04:14:15 +01:00
end
else
raise EMVCException. Create( 'Session already bounded for this request' ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
constructor TWebContext. Create(
const ARequest: TWebRequest;
const AResponse: TWebResponse;
const AConfig: TMVCConfig;
const ASerializers: TDictionary< string , IMVCSerializer> ) ;
2013-10-30 00:48:23 +01:00
begin
2015-12-02 04:14:15 +01:00
inherited Create;
2017-03-13 20:52:11 +01:00
FIsSessionStarted : = False ;
FSessionMustBeClose : = False ;
FWebSession : = nil ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
if IsLibrary then
begin
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
{$IFDEF WEBAPACHEHTTP}
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
if ARequest is TApacheRequest then
FRequest : = TMVCApacheWebRequest. Create( ARequest, ASerializers)
2017-04-14 16:43:31 +02:00
{$IFNDEF LINUX}
2017-03-13 20:52:11 +01:00
else if ARequest is TISAPIRequest then
FRequest : = TMVCISAPIWebRequest. Create( ARequest, ASerializers)
2017-04-14 16:43:31 +02:00
{$ENDIF}
2015-12-02 04:14:15 +01:00
else
2017-03-13 20:52:11 +01:00
raise EMVCException. Create( 'Unknown request type ' + ARequest. ClassName) ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
{$ELSE}
2015-12-02 04:14:15 +01:00
2017-04-14 16:43:31 +02:00
FRequest : = TMVCISAPIWebRequest. Create( ARequest, ASerializers)
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
{$ENDIF}
2015-04-01 17:01:23 +02:00
2017-03-13 20:52:11 +01:00
end
else
FRequest : = TMVCINDYWebRequest. Create( ARequest, ASerializers) ;
FResponse : = TMVCWebResponse. Create( AResponse) ;
FConfig : = AConfig;
FSerializers : = ASerializers;
FData : = TDictionary< string , string > . Create;
FLoggedUser : = nil ;
2013-11-05 14:57:50 +01:00
end ;
2017-03-13 20:52:11 +01:00
destructor TWebContext. Destroy;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
FResponse. Free;
FRequest. Free;
FData. Free;
if Assigned( FLoggedUser) then
FLoggedUser. Free;
inherited Destroy;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TWebContext. Flush;
2013-11-10 01:04:17 +01:00
begin
2017-03-13 20:52:11 +01:00
FResponse. Flush;
2013-11-10 01:04:17 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TWebContext. GetLoggedUser: TUser;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if not Assigned( FLoggedUser) then
FLoggedUser : = TUser. Create;
Result : = FLoggedUser;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TWebContext. GetParamsTable: TMVCRequestParamsTable;
2016-12-05 15:50:00 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FRequest. ParamsTable;
2016-12-05 15:50:00 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TWebContext. GetWebSession: TWebSession;
2016-02-27 09:58:54 +01:00
begin
2017-03-13 20:52:11 +01:00
if not Assigned( FWebSession) then
begin
FWebSession : = TMVCEngine. GetCurrentSession( StrToInt64( FConfig[ TMVCConfigKey. SessionTimeout] ) , TMVCEngine. ExtractSessionIdFromWebRequest( FRequest. RawWebRequest) , False ) ;
if not Assigned( FWebSession) then
SessionStart
else
TMVCEngine. SendSessionCookie( Self, FWebSession. SessionId) ;
2016-02-27 09:58:54 +01:00
end ;
2017-03-13 20:52:11 +01:00
Result : = FWebSession;
Result . MarkAsUsed;
2016-02-27 09:58:54 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TWebContext. IsSessionStarted: Boolean ;
2017-02-08 11:42:05 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = FIsSessionStarted;
2017-02-08 11:42:05 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TWebContext. SendSessionCookie( const AContext: TWebContext) : string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = TMVCEngine. SendSessionCookie( Self) ;
2015-10-18 16:35:50 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TWebContext. SessionId: string ;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( FWebSession) then
Exit( FWebSession. SessionId) ;
Result : = FRequest. Cookie( TMVCConstants. SESSION_TOKEN_NAME) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TWebContext. SessionMustBeClose: Boolean ;
2016-10-02 17:44:20 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = FSessionMustBeClose;
2016-10-02 17:44:20 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TWebContext. SessionStart;
var
Id: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if not Assigned( FWebSession) then
begin
Id : = TMVCEngine. SendSessionCookie( Self) ;
FWebSession : = AddSessionToTheSessionList( Config[ TMVCConfigKey. SessionType] , Id, StrToInt64( Config[ TMVCConfigKey. SessionTimeout] ) ) ;
FIsSessionStarted : = True ;
FSessionMustBeClose : = False ;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TWebContext. SessionStarted: Boolean ;
var
SId: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
SId : = SessionId;
if SId. IsEmpty then
Exit( False ) ;
2017-03-23 18:51:25 +01:00
TMonitor. Enter( GlobalSessionList) ;
2017-03-13 20:52:11 +01:00
try
2017-03-23 18:51:25 +01:00
Result : = GlobalSessionList. ContainsKey( SId) ;
2017-03-13 20:52:11 +01:00
finally
2017-03-23 18:51:25 +01:00
TMonitor. Exit( GlobalSessionList) ;
2017-03-13 20:52:11 +01:00
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TWebContext. SessionStop( const ARaiseExceptionIfExpired: Boolean ) ;
var
Cookie: TCookie;
SId: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
FResponse. Cookies. Clear;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
Cookie : = FResponse. Cookies. Add;
Cookie. Name : = TMVCConstants. SESSION_TOKEN_NAME;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
Cookie. Value : = GUIDToString( TGUID. NewGuid) + 'invalid' + GUIDToString( TGUID. NewGuid) ;
Cookie. Expires : = EncodeDate( 1 9 7 0 , 1 , 1 ) ;
Cookie. Path : = '/' ;
2017-03-23 18:51:25 +01:00
TMonitor. Enter( GlobalSessionList) ;
2015-12-02 04:14:15 +01:00
try
2017-03-13 20:52:11 +01:00
SId : = TMVCEngine. ExtractSessionIdFromWebRequest( FRequest. RawWebRequest) ;
2017-03-23 18:51:25 +01:00
GlobalSessionList. Remove( SId) ;
2017-03-13 20:52:11 +01:00
finally
2017-03-23 18:51:25 +01:00
TMonitor. Exit( GlobalSessionList) ;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
FIsSessionStarted : = False ;
FSessionMustBeClose : = True ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TWebContext. SetParamsTable( const AValue: TMVCRequestParamsTable) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
FRequest. ParamsTable : = AValue;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ TMVCEngine }
function TMVCEngine. AddController( const AControllerClazz: TMVCControllerClazz) : TMVCEngine;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = AddController( AControllerClazz, nil ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCEngine. AddController( const AControllerClazz: TMVCControllerClazz; const ACreateAction: TMVCControllerCreateAction) : TMVCEngine;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
FControllers. Add( TMVCControllerDelegate. Create( AControllerClazz, ACreateAction) ) ;
Result : = Self;
end ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
function TMVCEngine. AddMiddleware( const AMiddleware: IMVCMiddleware) : TMVCEngine;
begin
FMiddlewares. Add( AMiddleware) ;
Result : = Self;
2015-10-18 16:35:50 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCEngine. AddSerializer( const AContentType: string ; const ASerializer: IMVCSerializer) : TMVCEngine;
2015-12-02 04:14:15 +01:00
begin
2017-04-14 16:43:31 +02:00
FSerializers. AddOrSetValue( AContentType, ASerializer) ;
2017-03-13 20:52:11 +01:00
Result : = Self;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
class procedure TMVCEngine. ClearSessionCookiesAlreadySet( const ACookies: TCookieCollection) ;
var
I: Integer ;
SessionCookieName: string ;
Cookie: TCookie;
2016-09-25 16:17:37 +02:00
begin
2017-03-13 20:52:11 +01:00
SessionCookieName : = TMVCConstants. SESSION_TOKEN_NAME. ToLower;
I : = 0 ;
while true do
begin
if I = ACookies. Count then
Break;
Cookie : = ACookies[ I] ;
if LowerCase( Cookie. Name ) = SessionCookieName then
ACookies. Delete( I)
else
Inc( I) ;
end ;
2016-09-25 16:17:37 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. ConfigDefaultValues;
2013-12-04 13:06:18 +01:00
begin
2017-03-13 20:52:11 +01:00
Log. Info( 'ENTER: Config default values' , LOGGERPRO_TAG) ;
2013-12-04 13:06:18 +01:00
2017-03-13 20:52:11 +01:00
Config[ TMVCConfigKey. SessionTimeout] : = '30' { 30 minutes } ;
Config[ TMVCConfigKey. DocumentRoot] : = '.\www' ;
Config[ TMVCConfigKey. FallbackResource] : = '' ;
Config[ TMVCConfigKey. DefaultContentType] : = TMVCConstants. DEFAULT_CONTENT_TYPE;
Config[ TMVCConfigKey. DefaultContentCharset] : = TMVCConstants. DEFAULT_CONTENT_CHARSET;
Config[ TMVCConfigKey. DefaultViewFileExtension] : = 'html' ;
Config[ TMVCConfigKey. ViewPath] : = 'templates' ;
Config[ TMVCConfigKey. ISAPIPath] : = '' ;
Config[ TMVCConfigKey. StompServer] : = 'localhost' ;
Config[ TMVCConfigKey. StompServerPort] : = '61613' ;
Config[ TMVCConfigKey. StompUsername] : = 'guest' ;
Config[ TMVCConfigKey. StompPassword] : = 'guest' ;
Config[ TMVCConfigKey. AllowUnhandledAction] : = 'false' ;
Config[ TMVCConfigKey. ServerName] : = 'DelphiMVCFramework' ;
Config[ TMVCConfigKey. ExposeServerSignature] : = 'true' ;
Config[ TMVCConfigKey. SessionType] : = 'memory' ;
Config[ TMVCConfigKey. IndexDocument] : = 'index.html' ;
2017-02-09 11:24:18 +01:00
2017-03-13 20:52:11 +01:00
FMediaTypes. Add( '.html' , TMVCMediaType. TEXT_HTML) ;
FMediaTypes. Add( '.htm' , TMVCMediaType. TEXT_HTML) ;
FMediaTypes. Add( '.txt' , TMVCMediaType. TEXT_PLAIN) ;
FMediaTypes. Add( '.css' , TMVCMediaType. TEXT_CSS) ;
FMediaTypes. Add( '.js' , TMVCMediaType. TEXT_JAVASCRIPT) ;
FMediaTypes. Add( '.jpg' , TMVCMediaType. IMAGE_JPEG) ;
FMediaTypes. Add( '.jpeg' , TMVCMediaType. IMAGE_JPEG) ;
FMediaTypes. Add( '.png' , TMVCMediaType. IMAGE_PNG) ;
FMediaTypes. Add( '.appcache' , TMVCMediaType. TEXT_CACHEMANIFEST) ;
2017-02-09 11:24:18 +01:00
2017-03-13 20:52:11 +01:00
Log. Info( 'EXIT: Config default values' , LOGGERPRO_TAG) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
constructor TMVCEngine. Create(
const AWebModule: TWebModule;
const AConfigAction: TProc< TMVCConfig> ;
const ACustomLogger: ILogWriter) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
inherited Create;
FWebModule : = AWebModule;
FConfig : = TMVCConfig. Create;
2017-04-14 16:43:31 +02:00
FSerializers : = TDictionary< string , IMVCSerializer> . Create;
2017-03-13 20:52:11 +01:00
FMiddlewares : = TList< IMVCMiddleware> . Create;
FControllers : = TObjectList< TMVCControllerDelegate> . Create( True ) ;
FMediaTypes : = TDictionary< string , string > . Create;
FApplicationSession : = nil ;
FSavedOnBeforeDispatch : = nil ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
WebRequestHandler. CacheConnections : = True ;
WebRequestHandler. MaxConnections : = 4 0 9 6 ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
FixUpWebModule;
MVCFramework. Logger. SetDefaultLogger( ACustomLogger) ;
ConfigDefaultValues;
2016-11-18 18:25:09 +01:00
2017-03-13 20:52:11 +01:00
if Assigned( AConfigAction) then
2016-11-18 18:25:09 +01:00
begin
2017-03-13 20:52:11 +01:00
LogEnterMethod( 'Custom configuration method' ) ;
AConfigAction( FConfig) ;
LogExitMethod( 'Custom configuration method' ) ;
2016-11-18 18:25:09 +01:00
end ;
2017-04-14 16:43:31 +02:00
RegisterDefaultsSerializers;
2017-03-13 20:52:11 +01:00
LoadSystemControllers;
2016-06-28 13:42:14 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. DefineDefaultReponseHeaders( const AContext: TWebContext) ;
2016-04-03 22:35:27 +02:00
begin
2017-03-13 20:52:11 +01:00
if Config[ TMVCConfigKey. ExposeServerSignature] = 'true' then
AContext. Response. CustomHeaders. Values[ 'Server' ] : = Config[ TMVCConfigKey. ServerName] ;
AContext. Response. RawWebResponse. Date : = Now;
2016-04-03 22:35:27 +02:00
end ;
2017-03-13 20:52:11 +01:00
destructor TMVCEngine. Destroy;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
FConfig. Free;
2017-04-14 16:43:31 +02:00
FSerializers. Free;
2017-03-13 20:52:11 +01:00
FMiddlewares. Free;
FControllers. Free;
FMediaTypes. Free;
inherited Destroy;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCEngine. ExecuteAction( const ASender: TObject; const ARequest: TWebRequest; const AResponse: TWebResponse) : Boolean ;
2016-06-28 13:42:14 +02:00
var
2017-03-13 20:52:11 +01:00
LParamsTable: TMVCRequestParamsTable;
LContext: TWebContext;
LFileName: string ;
LRouter: TMVCRouter;
LHandled: Boolean ;
LResponseContentType: string ;
LResponseContentCharset: string ;
LSelectedController: TMVCController;
LActionFormalParams: TArray< TRttiParameter> ;
LActualParams: TArray< TValue> ;
begin
Result : = False ;
LParamsTable : = TMVCRequestParamsTable. Create;
2016-06-28 13:42:14 +02:00
try
2017-04-14 16:43:31 +02:00
LContext : = TWebContext. Create( ARequest, AResponse, FConfig, FSerializers) ;
2017-03-13 20:52:11 +01:00
try
DefineDefaultReponseHeaders( LContext) ;
if IsStaticFileRequest( ARequest, LFileName) then
Result : = SendStaticFileIfPresent( LContext, LFileName)
else
begin
LHandled : = False ;
LRouter : = TMVCRouter. Create( FConfig) ;
try
ExecuteBeforeRoutingMiddleware( LContext, LHandled) ;
if not LHandled then
begin
if LRouter. ExecuteRouting(
ARequest. PathInfo,
TMVCRouter. StringMethodToHTTPMetod( ARequest. Method) ,
ARequest. ContentType,
ARequest. Accept,
FControllers,
FConfig[ TMVCConfigKey. DefaultContentType] ,
FConfig[ TMVCConfigKey. DefaultContentCharset] ,
LParamsTable,
LResponseContentType,
LResponseContentCharset) then
begin
if Assigned( LRouter. ControllerCreateAction) then
LSelectedController : = LRouter. ControllerCreateAction( )
else
LSelectedController : = LRouter. ControllerClazz. Create;
try
LSelectedController. Engine : = Self;
LSelectedController. Context : = LContext;
LSelectedController. ApplicationSession : = FApplicationSession;
LContext. ParamsTable : = LParamsTable;
2016-06-28 13:42:14 +02:00
2017-03-13 20:52:11 +01:00
try
ExecuteBeforeControllerActionMiddleware( LContext, LRouter. ControllerClazz. QualifiedClassName, LRouter. MethodToCall. Name , LHandled) ;
if LHandled then
Exit( True ) ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
LSelectedController. MVCControllerAfterCreate;
try
LHandled : = False ;
LSelectedController. ContentType : = LResponseContentType;
LSelectedController. ContentCharset : = LResponseContentCharset;
if not LHandled then
begin
LActionFormalParams : = LRouter. MethodToCall. GetParameters;
if ( Length( LActionFormalParams) = 0 ) then
SetLength( LActualParams, 0 )
else if ( Length( LActionFormalParams) = 1 ) and ( SameText( LActionFormalParams[ 0 ] . ParamType. QualifiedName, 'MVCFramework.TWebContext' ) ) then
begin
SetLength( LActualParams, 1 ) ;
LActualParams[ 0 ] : = LContext;
end
else
FillActualParamsForAction( LContext, LActionFormalParams, LRouter. MethodToCall. Name , LActualParams) ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
LSelectedController. OnBeforeAction( LContext, LRouter. MethodToCall. Name , LHandled) ;
if not LHandled then
try
LRouter. MethodToCall. Invoke( LSelectedController, LActualParams) ;
finally
LSelectedController. OnAfterAction( LContext, LRouter. MethodToCall. Name ) ;
end ;
end ;
finally
LSelectedController. MVCControllerBeforeDestroy;
end ;
2017-03-20 21:42:28 +01:00
ExecuteAfterControllerActionMiddleware( LContext, LRouter. MethodToCall. Name , LHandled) ;
2017-03-13 20:52:11 +01:00
except
on E: EMVCSessionExpiredException do
begin
LogException( E, E. DetailedMessage) ;
LContext. SessionStop( false ) ;
LSelectedController. ResponseStatus( E. HTTPErrorCode) ;
LSelectedController. Render( E) ;
end ;
on E: EMVCException do
begin
LogException( E, E. DetailedMessage) ;
LSelectedController. ResponseStatus( E. HTTPErrorCode) ;
LSelectedController. Render( E) ;
end ;
on E: EInvalidOp do
begin
LogException( E, 'Invalid OP' ) ;
LSelectedController. ResponseStatus( HTTP_STATUS. InternalServerError) ;
LSelectedController. Render( E) ;
end ;
on E: Exception do
begin
LogException( E, 'Global Action Exception Handler' ) ;
LSelectedController. ResponseStatus( HTTP_STATUS. InternalServerError) ;
LSelectedController. Render( E) ;
end ;
end ;
LContext. Response. ContentType : = LSelectedController. ContentType;
Log( TLogLevel. levNormal, ARequest. Method + ':' +
ARequest. RawPathInfo + ' -> ' +
LRouter. ControllerClazz. QualifiedClassName + ' - ' +
IntToStr( AResponse. StatusCode) + ' ' + AResponse. ReasonString)
finally
LSelectedController. Free;
end ;
end
else
begin
if Config[ TMVCConfigKey. AllowUnhandledAction] = 'false' then
begin
if not Config[ TMVCConfigKey. FallbackResource] . IsEmpty then
Result : = SendStaticFileIfPresent( LContext, TPath. Combine( Config[ TMVCConfigKey. DocumentRoot] , Config[ TMVCConfigKey. FallbackResource] ) ) ;
if not Result then
begin
HTTP404( LContext) ;
Log( TLogLevel. levNormal, ARequest. Method + ':' +
ARequest. RawPathInfo + ' -> NO ACTION ' + ' - ' +
IntToStr( AResponse. StatusCode) + ' ' +
AResponse. ReasonString) ;
end ;
end
else
LContext. Response. FlushOnDestroy : = False ;
end ;
end ;
finally
LRouter. Free;
end ;
end ;
finally
LContext. Free;
end ;
2015-12-02 04:14:15 +01:00
finally
2017-03-13 20:52:11 +01:00
LParamsTable. Free;
2015-12-02 04:14:15 +01:00
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. ExecuteAfterControllerActionMiddleware(
const AContext: TWebContext;
const AActionName: string ;
const AHandled: Boolean ) ;
var
I: Integer ;
2013-11-05 14:57:50 +01:00
begin
2017-03-13 20:52:11 +01:00
for I : = FMiddlewares. Count - 1 downto 0 do
FMiddlewares[ I] . OnAfterControllerAction( AContext, AActionName, AHandled) ;
2013-11-05 14:57:50 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. ExecuteBeforeControllerActionMiddleware(
const AContext: TWebContext;
const AControllerQualifiedClassName: string ;
const AActionName: string ;
var AHandled: Boolean ) ;
var
Middleware: IMVCMiddleware;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if not AHandled then
for Middleware in FMiddlewares do
begin
Middleware. OnBeforeControllerAction( AContext, AControllerQualifiedClassName, AActionName, AHandled) ;
if AHandled then
Break;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. ExecuteBeforeRoutingMiddleware( const AContext: TWebContext; var AHandled: Boolean ) ;
var
Middleware: IMVCMiddleware;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if not AHandled then
for Middleware in FMiddlewares do
begin
Middleware. OnBeforeRouting( AContext, AHandled) ;
if AHandled then
Break;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
class function TMVCEngine. ExtractSessionIdFromWebRequest( const AWebRequest: TWebRequest) : string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = AWebRequest. CookieFields. Values[ TMVCConstants. SESSION_TOKEN_NAME] ;
if not Result . IsEmpty then
Result : = TIdURI. URLDecode( Result ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. FillActualParamsForAction(
const AContext: TWebContext;
const AActionFormalParams: TArray< TRttiParameter> ;
const AActionName: string ;
var AActualParams: TArray< TValue> ) ;
var
ParamName: string ;
I: Integer ;
StrValue: string ;
FormatSettings: TFormatSettings;
WasDateTime: Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if AContext. Request. SegmentParamsCount < > Length( AActionFormalParams) then
raise EMVCException. CreateFmt( 'Paramaters count mismatch (expected %d actual %d) for action "%s"' , [ Length( AActionFormalParams) , AContext. Request. SegmentParamsCount, AActionName] ) ;
SetLength( AActualParams, Length( AActionFormalParams) ) ;
for I : = 0 to Length( AActionFormalParams) - 1 do
begin
ParamName : = AActionFormalParams[ I] . Name ;
if not AContext. Request. SegmentParam( ParamName, StrValue) then
raise EMVCException. CreateFmt( 'Invalid paramater %s for action %s (Hint: Here parameters names are case-sensitive)' , [ ParamName, AActionName] ) ;
case AActionFormalParams[ I] . ParamType. TypeKind of
tkInteger, tkInt64:
begin
AActualParams[ I] : = StrToInt( StrValue) ;
end ;
tkUString:
begin
AActualParams[ I] : = StrValue;
end ;
tkFloat:
begin
WasDateTime : = False ;
if AActionFormalParams[ I] . ParamType. QualifiedName = 'System.TDate' then
begin
try
WasDateTime : = True ;
AActualParams[ I] : = ISODateToDate( StrValue) ;
except
raise EMVCException. CreateFmt( 'Invalid TDate value for param [%s]' , [ AActionFormalParams[ I] . Name ] ) ;
end ;
end
else if AActionFormalParams[ I] . ParamType. QualifiedName = 'System.TDateTime' then
begin
try
WasDateTime : = True ;
AActualParams[ I] : = ISOTimeStampToDateTime( StrValue) ;
except
raise EMVCException. CreateFmt( 'Invalid TDateTime value for param [%s]' , [ AActionFormalParams[ I] . Name ] ) ;
end ;
end
else if AActionFormalParams[ I] . ParamType. QualifiedName = 'System.TTime' then
begin
try
WasDateTime : = True ;
AActualParams[ I] : = ISOTimeToTime( StrValue) ;
except
raise EMVCException. CreateFmt( 'Invalid TTime value for param [%s]' , [ AActionFormalParams[ I] . Name ] ) ;
end ;
end ;
if not WasDateTime then
begin
FormatSettings. DecimalSeparator : = '.' ;
AActualParams[ I] : = StrToFloat( StrValue, FormatSettings) ;
end ;
end ;
tkEnumeration:
begin
if AActionFormalParams[ I] . ParamType. QualifiedName = 'System.Boolean' then
begin
if SameText( StrValue, 'true' ) or SameText( StrValue, '1' ) then
AActualParams[ I] : = True
else if SameText( StrValue, 'false' ) or SameText( StrValue, '0' ) then
AActualParams[ I] : = False
else
raise EMVCException. CreateFmt
( 'Invalid boolean value for parameter %s. Boolean parameters accepts only "true"/"false" or "1"/"0".' , [ ParamName] ) ;
end
else
raise EMVCException. CreateFmt
( 'Invalid type for parameter %s. Allowed types are ' + ALLOWED_TYPED_ACTION_PARAMETERS_TYPES, [ ParamName] ) ;
end ;
else
begin
raise EMVCException. CreateFmt
( 'Invalid type for parameter %s. Allowed types are ' + ALLOWED_TYPED_ACTION_PARAMETERS_TYPES, [ ParamName] ) ;
end ;
end ;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. FixUpWebModule;
2016-06-23 11:42:16 +02:00
begin
2017-03-13 20:52:11 +01:00
FSavedOnBeforeDispatch : = FWebModule. BeforeDispatch;
FWebModule. BeforeDispatch : = OnBeforeDispatch;
2016-06-23 11:42:16 +02:00
end ;
2017-03-13 20:52:11 +01:00
class function TMVCEngine. GetCurrentSession(
const ASessionTimeout: Integer ;
const ASessionId: string ;
const ARaiseExceptionIfExpired: Boolean ) : TWebSession;
var
List: TObjectDictionary< string , TWebSession> ;
IsExpired: Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = nil ;
2017-03-23 18:51:25 +01:00
List : = GlobalSessionList;
2017-03-13 20:52:11 +01:00
TMonitor. Enter( List) ;
try
if not ASessionId. IsEmpty then
begin
IsExpired : = True ;
if List. TryGetValue( ASessionId, Result ) then
if ( ASessionTimeout = 0 ) then
IsExpired : = MinutesBetween( Now, Result . LastAccess) > DEFAULT_SESSION_INACTIVITY
else
IsExpired : = MinutesBetween( Now, Result . LastAccess) > ASessionTimeout;
if Assigned( Result ) then
if IsExpired then
begin
List. Remove( ASessionId) ;
if ARaiseExceptionIfExpired then
raise EMVCSessionExpiredException. Create( 'Session expired.' )
else
Result : = nil ;
end
else
Result . MarkAsUsed;
end ;
finally
TMonitor. Exit( List) ;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCEngine. GetSessionBySessionId( const ASessionId: string ) : TWebSession;
2016-05-11 10:39:20 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = TMVCEngine. GetCurrentSession( StrToInt64( Config[ TMVCConfigKey. SessionTimeout] ) , ASessionId, False ) ;
if Assigned( Result ) then
Result . MarkAsUsed;
2016-05-11 10:39:20 +02:00
end ;
2017-04-14 16:43:31 +02:00
function TMVCEngine. GetViewEngineClass: TMVCViewEngineClass;
begin
if FViewEngineClass = nil then
raise EMVCConfigException. Create( 'No View Engine configured. [HINT: Use TMVCEngine.SetViewEngine() to set a valid view engine]' ) ;
Result : = FViewEngineClass;
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. HTTP404( const AContext: TWebContext) ;
2013-11-08 23:10:25 +01:00
begin
2017-03-13 20:52:11 +01:00
AContext. Response. StatusCode : = HTTP_STATUS. NotFound;
AContext. Response. ReasonString : = 'Not Found' ;
AContext. Response. Content : = 'Not Found' ;
2013-11-08 23:10:25 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. HTTP500( const AContext: TWebContext; const AReasonString: string ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
AContext. Response. StatusCode : = HTTP_STATUS. InternalServerError; ;
AContext. Response. ReasonString : = 'Internal server error: ' + AReasonString;
AContext. Response. Content : = 'Internal server error: ' + AReasonString;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCEngine. IsStaticFileRequest( const ARequest: TWebRequest; out AFileName: string ) : Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = ( not FConfig[ TMVCConfigKey. DocumentRoot] . IsEmpty) and ( TMVCStaticContents. IsStaticFile( TPath. Combine( AppPath, FConfig[ TMVCConfigKey. DocumentRoot] ) , ARequest. PathInfo, AFileName) ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. LoadSystemControllers;
2014-04-10 13:56:23 +02:00
begin
2017-03-13 20:52:11 +01:00
Log( TLogLevel. levNormal, 'ENTER: LoadSystemControllers' ) ;
AddController( TMVCSystemController) ;
Log( TLogLevel. levNormal, 'EXIT: LoadSystemControllers' ) ;
2014-04-10 13:56:23 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. OnBeforeDispatch( ASender: TObject; ARequest: TWebRequest; AResponse: TWebResponse; var AHandled: Boolean ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
AHandled : = False ;
if Assigned( FSavedOnBeforeDispatch) then
FSavedOnBeforeDispatch( ASender, ARequest, AResponse, AHandled) ;
if not AHandled then
begin
try
AHandled : = ExecuteAction( ASender, ARequest, AResponse) ;
except
on E: Exception do
begin
LogException( E) ;
AResponse. Content : = E. Message ;
AResponse. SendResponse;
AHandled : = True ;
end ;
end ;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-04-14 16:43:31 +02:00
procedure TMVCEngine. RegisterDefaultsSerializers;
2013-10-30 00:48:23 +01:00
begin
2017-04-14 16:43:31 +02:00
FSerializers. Add( TMVCMediaType. APPLICATION_JSON, TMVCJSONSerializer. Create) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCEngine. ResponseErrorPage( const AException: Exception; const ARequest: TWebRequest; const AResponse: TWebResponse) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
AResponse. SetCustomHeader( 'x-mvc-error' , AException. ClassName + ': ' + AException. Message ) ;
AResponse. StatusCode : = HTTP_STATUS. OK;
begin
AResponse. ContentType : = TMVCMediaType. TEXT_PLAIN;
AResponse. Content : = Config[ TMVCConfigKey. ServerName] + ' ERROR:' +
sLineBreak + 'Exception raised of class: ' + AException. ClassName + sLineBreak +
'***********************************************' + sLineBreak + AException. Message
+ sLineBreak + '***********************************************' ;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
class function TMVCEngine. SendSessionCookie( const AContext: TWebContext) : string ;
var
SId: string ;
2016-06-22 17:49:16 +02:00
begin
2017-03-13 20:52:11 +01:00
SId : = StringReplace( StringReplace( StringReplace( GUIDToString( TGUID. NewGuid) , '}' , '' , [ ] ) , '{' , '' , [ ] ) , '-' , '' , [ rfReplaceAll] ) ;
Result : = SendSessionCookie( AContext, SId) ;
2016-06-22 17:49:16 +02:00
end ;
2017-03-13 20:52:11 +01:00
class function TMVCEngine. SendSessionCookie( const AContext: TWebContext; const ASessionId: string ) : string ;
var
Cookie: TCookie;
SessionTimeout: Integer ;
2016-06-22 17:49:16 +02:00
begin
2017-03-13 20:52:11 +01:00
ClearSessionCookiesAlreadySet( AContext. Response. Cookies) ;
Cookie : = AContext. Response. Cookies. Add;
Cookie. Name : = TMVCConstants. SESSION_TOKEN_NAME;
Cookie. Value : = ASessionId;
SessionTimeout : = StrToIntDef( AContext. Config[ TMVCConfigKey. SessionTimeout] , 0 ) ;
if SessionTimeout = 0 then
Cookie. Expires : = 0
2016-06-22 17:49:16 +02:00
else
2017-03-13 20:52:11 +01:00
Cookie. Expires : = Now + OneMinute * SessionTimeout;
Cookie. Path : = '/' ;
Result : = ASessionId;
2016-06-22 17:49:16 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCEngine. SendStaticFileIfPresent( const AContext: TWebContext; const AFileName: String ) : Boolean ;
var
LContentType: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = False ;
if TFile. Exists( AFileName) then
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
if FMediaTypes. TryGetValue( LowerCase( ExtractFileExt( AFileName) ) , LContentType) then
LContentType : = lContentType + ';charset=' + FConfig[ TMVCConfigKey. DefaultContentCharset]
else
LContentType : = TMVCMediaType. APPLICATION_OCTETSTREAM;
TMVCStaticContents. SendFile( AFileName, LContentType, AContext) ;
Result : = True ;
2015-12-02 04:14:15 +01:00
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-04-14 16:43:31 +02:00
function TMVCEngine. SetViewEngine(
const AViewEngineClass: TMVCViewEngineClass) : TMVCEngine;
begin
FViewEngineClass : = AViewEngineClass;
Result : = Self;
end ;
2017-03-13 20:52:11 +01:00
{ TMVCBase }
class function TMVCBase. GetApplicationFileName: string ;
2017-04-14 16:43:31 +02:00
// var
// Name: PChar;
// Size: Integer;
begin
Result : = GetModuleName( HInstance) ;
// Result := EmptyStr;
// Name := GetMemory(2048);
// try
// GetModuleName()
// Size := GetModuleFileName(0, Name, 2048);
// if Size > 0 then
// Result := Name;
// finally
// FreeMem(Name, 2048);
// end;
2016-09-27 14:33:51 +02:00
end ;
2017-03-13 20:52:11 +01:00
class function TMVCBase. GetApplicationFileNamePath: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = IncludeTrailingPathDelimiter( ExtractFilePath( GetApplicationFileName) ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCBase. GetApplicationSession: TWebApplicationSession;
2013-11-12 01:23:50 +01:00
begin
2017-03-13 20:52:11 +01:00
if not Assigned( FApplicationSession) then
raise EMVCException. CreateFmt( 'ApplicationSession not assigned to this %s instance.' , [ ClassName] ) ;
Result : = FApplicationSession;
end ;
2013-11-12 01:23:50 +01:00
2017-03-13 20:52:11 +01:00
function TMVCBase. GetConfig: TMVCConfig;
begin
Result : = Engine. Config;
2013-11-12 01:23:50 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCBase. GetEngine: TMVCEngine;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if not Assigned( FEngine) then
raise EMVCException. CreateFmt( 'MVCEngine not assigned to this %s instance.' , [ ClassName] ) ;
Result : = FEngine;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCBase. SetApplicationSession( const AValue: TWebApplicationSession) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
FApplicationSession : = AValue;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCBase. SetEngine( const AValue: TMVCEngine) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
FEngine : = AValue;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ TMVCControllerDelegate }
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
constructor TMVCControllerDelegate. Create( const AClazz: TMVCControllerClazz; const ACreateAction: TMVCControllerCreateAction) ;
2013-10-30 00:48:23 +01:00
begin
2015-12-02 04:14:15 +01:00
inherited Create;
2017-03-13 20:52:11 +01:00
FClazz : = AClazz;
FCreateAction : = ACreateAction;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ TMVCStaticContents }
class function TMVCStaticContents. IsScriptableFile( const AStaticFileName: string ; const AConfig: TMVCConfig) : Boolean ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = TPath. GetExtension( AStaticFileName) . ToLower = '.' + AConfig[ TMVCConfigKey. DefaultViewFileExtension] . ToLower;
end ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
class function TMVCStaticContents. IsStaticFile( const AViewPath, AWebRequestPath: string ; out ARealFileName: string ) : Boolean ;
var
FileName: string ;
begin
if TDirectory. Exists( AViewPath) then
FileName : = AViewPath + AWebRequestPath. Replace( '/' , TPath. DirectorySeparatorChar)
2015-12-02 04:14:15 +01:00
else
2017-03-13 20:52:11 +01:00
FileName : = GetApplicationFileNamePath + AViewPath + AWebRequestPath. Replace( '/' , TPath. DirectorySeparatorChar) ;
ARealFileName : = FileName;
Result : = TFile. Exists( ARealFileName) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
class procedure TMVCStaticContents. SendFile( const AFileName, AMediaType: string ; AContext: TWebContext) ;
2013-10-30 00:48:23 +01:00
var
2017-03-13 20:52:11 +01:00
FileDate: TDateTime;
ReqDate: TDateTime;
2015-12-02 04:14:15 +01:00
S: TFileStream;
begin
2017-03-13 20:52:11 +01:00
FileDate : = IndyFileAge( AFileName) ;
if ( FileDate = 0.0 ) and ( not FileExists( AFileName) ) then
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
AContext. Response. StatusCode : = 4 0 4 ;
2015-12-02 04:14:15 +01:00
end
else
begin
2017-03-13 20:52:11 +01:00
ReqDate : = GMTToLocalDateTime( AContext. Request. Headers[ 'If-Modified-Since' ] ) ;
if ( ReqDate < > 0 ) and ( abs( ReqDate - FileDate) < 2 * ( 1 / ( 2 4 * 6 0 * 6 0 ) ) )
2015-12-02 04:14:15 +01:00
then
begin
2017-03-13 20:52:11 +01:00
AContext. Response. ContentType : = AMediaType;
AContext. Response. StatusCode : = 3 0 4 ;
2015-12-02 04:14:15 +01:00
end
else
begin
S : = TFileStream. Create( AFileName, fmOpenRead or fmShareDenyNone) ;
2017-03-13 20:52:11 +01:00
AContext. Response. SetCustomHeader( 'Last-Modified' , LocalDateTimeToHttpStr( FileDate) ) ;
AContext. Response. SetContentStream( S, AMediaType) ;
2015-12-02 04:14:15 +01:00
end ;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
{ TMVCController }
constructor TMVCController. Create;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
inherited Create;
FContext : = nil ;
FContentCharset : = TMVCConstants. DEFAULT_CONTENT_CHARSET;
FResponseStream : = nil ;
2017-03-20 19:08:01 +01:00
FViewModel : = nil ;
FViewDataSets : = nil ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
destructor TMVCController. Destroy;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( FResponseStream) then
FResponseStream. Free;
2017-03-20 19:08:01 +01:00
if Assigned( FViewModel) then
FViewModel. Free;
if Assigned( FViewDataSets) then
FViewDataSets. Free;
2017-03-13 20:52:11 +01:00
inherited Destroy;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. GetClientId: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = Session[ CLIENTID_KEY] ;
if Result . IsEmpty then
raise EMVCException. Create( 'Invalid ClientID' + sLineBreak +
'Hint: Messaging extensions require a valid clientid. Did you call /messages/clients/YOUR_CLIENT_ID ?' ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. GetContentType: string ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = GetContext. Response. ContentType;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. GetContext: TWebContext;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if not Assigned( FContext) then
raise EMVCException. CreateFmt( 'Context already set on %s.' , [ ClassName] ) ;
Result : = FContext;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. GetCurrentWebModule: TWebModule;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = Engine. WebModule;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. GetSession: TWebSession;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = GetContext. Session;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. GetStatusCode: Integer ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Result : = GetContext. Response. StatusCode;
2013-10-30 00:48:23 +01:00
end ;
2017-03-20 19:08:01 +01:00
function TMVCController. GetViewDataSets: TObjectDictionary< string , TDataSet> ;
begin
if not Assigned( FViewDataSets) then
FViewDataSets : = TObjectDictionary< string , TDataSet> . Create;
Result : = FViewDataSets;
end ;
function TMVCController. GetViewModel: TMVCViewDataObject;
begin
if not Assigned( FViewModel) then
FViewModel : = TMVCViewDataObject. Create;
Result : = FViewModel;
end ;
function TMVCController. LoadView( const AViewNames: TArray< string > ) : string ;
begin
try
Result : = GetRenderedView( AViewNames) ;
ResponseStream. Append( Result ) ;
except
on E: Exception do
begin
LogException( E) ;
ContentType : = TMVCMediaType. TEXT_PLAIN;
Render( E) ;
end ;
end ;
end ;
procedure TMVCController. LoadViewFragment( const AViewFragment: string ) ;
begin
ResponseStream. Append( AViewFragment) ;
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. MVCControllerAfterCreate;
begin
{ Implement if need be. }
end ;
2014-05-14 15:55:41 +02:00
2017-03-13 20:52:11 +01:00
procedure TMVCController. MVCControllerBeforeDestroy;
begin
{ Implement if need be. }
end ;
2016-05-11 10:39:20 +02:00
2017-03-13 20:52:11 +01:00
procedure TMVCController. OnAfterAction( AContext: TWebContext; const AActionName: string ) ;
2014-05-14 15:55:41 +02:00
begin
2017-03-13 20:52:11 +01:00
{ Implement if need be. }
2014-05-14 15:55:41 +02:00
end ;
2013-10-30 00:48:23 +01:00
2017-03-13 20:52:11 +01:00
procedure TMVCController. OnBeforeAction( AContext: TWebContext; const AActionName: string ; var AHandled: Boolean ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
AHandled : = False ;
if ContentType. IsEmpty then
ContentType : = Config[ TMVCConfigKey. DefaultContentType] ;
{ Implement if need be. }
2013-10-30 00:48:23 +01:00
end ;
2017-03-20 19:08:01 +01:00
procedure TMVCController. PushDataSetToView( const AModelName: string ; const ADataSet: TDataSet) ;
var
LSerializer: IMVCSerializer;
begin
LSerializer : = TMVCJSONSerializer. Create;
PushToView( AModelName, LSerializer. SerializeDataSet( ADataSet) ) ;
end ;
procedure TMVCController. PushObjectToView( const AModelName: string ; const AModel: TObject) ;
var
LSerializer: IMVCSerializer;
begin
LSerializer : = TMVCJSONSerializer. Create;
PushToView( AModelName, LSerializer. SerializeObject( AModel) ) ;
end ;
procedure TMVCController. PushToView( const AModelName: string ; const AModel: string ) ;
begin
GetViewModel. Add( AModelName, AModel) ;
end ;
2014-05-05 18:52:49 +02:00
procedure TMVCController. RaiseSessionExpired;
begin
2017-03-13 20:52:11 +01:00
raise EMVCSessionExpiredException. Create( 'Session expired.' ) ;
2014-05-05 18:52:49 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Redirect( const AUrl: string ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
GetContext. Response. RawWebResponse. SendRedirect( AUrl) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const AObject: TObject; const AOwns: Boolean ) ;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
Render( AObject, AOwns, stDefault) ;
end ;
2015-12-02 04:14:15 +01:00
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const AContent: string ) ;
var
LContentType: string ;
OutEncoding: TEncoding;
begin
LContentType : = ContentType + '; charset=' + ContentCharset;
GetContext. Response. RawWebResponse. ContentType : = LContentType;
OutEncoding : = TEncoding. GetEncoding( ContentCharset) ;
try
if SameText( 'UTF-8' , UpperCase( ContentCharset) ) then
GetContext. Response. SetContentStream( TStringStream. Create( AContent, TEncoding. UTF8) , LContentType)
2015-12-02 04:14:15 +01:00
else
begin
2017-03-13 20:52:11 +01:00
GetContext. Response. SetContentStream(
TBytesStream. Create(
TEncoding. Convert( TEncoding. Default , OutEncoding, TEncoding. Default . GetBytes( AContent) ) ) ,
LContentType
) ;
2015-12-02 04:14:15 +01:00
end ;
2017-03-13 20:52:11 +01:00
finally
OutEncoding. Free;
2015-12-02 04:14:15 +01:00
end ;
2015-10-18 16:35:50 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render< T> ( const ACollection: TObjectList< T> ; const AOwns: Boolean ) ;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
Self. Render< T> ( ACollection, AOwns, stDefault) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. ResponseStatus( const AStatusCode: Integer ; const AReasonString: string ) ;
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
StatusCode : = AStatusCode;
GetContext. Response. ReasonString : = AReasonString;
end ;
function TMVCController. ResponseStream: TStringBuilder;
begin
if not Assigned( FResponseStream) then
FResponseStream : = TStringBuilder. Create;
Result : = FResponseStream;
2015-10-18 16:35:50 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. Serializer: IMVCSerializer;
2015-10-18 16:35:50 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = Serializer( ContentType) ;
2015-10-18 16:35:50 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. SendFile( const AFileName: string ) ;
2016-09-29 18:17:12 +02:00
begin
2017-03-13 20:52:11 +01:00
TMVCStaticContents. SendFile( AFileName, ContentType, Context) ;
2016-09-29 18:17:12 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. SendStream(
const AStream: TStream;
const AOwns: Boolean ;
const ARewind: Boolean ) ;
2013-10-30 00:48:23 +01:00
var
2017-03-13 20:52:11 +01:00
S: TStream;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if ARewind then
AStream. Position : = 0 ;
2017-02-08 18:29:52 +01:00
2017-03-13 20:52:11 +01:00
if not AOwns then
begin
S : = TMemoryStream. Create;
S. CopyFrom( AStream, 0 ) ;
S. Position : = 0 ;
end
else
S : = AStream;
2017-02-08 18:29:52 +01:00
2017-03-13 20:52:11 +01:00
GetContext. Response. RawWebResponse. Content : = EmptyStr;
GetContext. Response. RawWebResponse. ContentType : = ContentType;
GetContext. Response. RawWebResponse. ContentStream : = S;
GetContext. Response. RawWebResponse. FreeContentStream : = True ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. Serializer( const AContentType: string ) : IMVCSerializer;
2014-04-15 17:03:47 +02:00
begin
2017-04-14 16:43:31 +02:00
if not Engine. Serializers. ContainsKey( AContentType) then
2017-03-13 20:52:11 +01:00
raise EMVCException. CreateFmt( 'The serializer for %s could not be found.' , [ AContentType] ) ;
2017-04-14 16:43:31 +02:00
Result : = Engine. Serializers. Items[ AContentType] ;
2014-04-15 17:03:47 +02:00
end ;
2017-03-13 20:52:11 +01:00
function TMVCController. SessionAs< T> : T;
2016-04-22 09:46:21 +02:00
begin
2017-03-13 20:52:11 +01:00
Result : = Session as T;
2016-04-22 09:46:21 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. SetContentType( const AValue: string ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
GetContext. Response. ContentType : = AValue;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. SetStatusCode( const AValue: Integer ) ;
2017-01-05 12:44:34 +01:00
begin
2017-03-13 20:52:11 +01:00
GetContext. Response. StatusCode : = AValue;
2017-01-05 12:44:34 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const AObject: TObject; const AOwns: Boolean ; const AType: TMVCSerializationType) ;
2017-01-05 12:44:34 +01:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( AObject) then
begin
try
Render( Serializer( ContentType) . SerializeObject( AObject, AType) ) ;
finally
if AOwns then
AObject. Free;
end ;
end
else
raise EMVCException. Create( 'Can not render an empty object.' ) ;
2017-01-05 12:44:34 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const AStream: TStream; const AOwns: Boolean ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
SendStream( AStream, AOwns) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const AErrorCode: Integer ; const AErrorMessage, AErrorClassName: string ) ;
var
R: TMVCErrorResponse;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
ResponseStatus( AErrorCode, AErrorMessage) ;
R : = TMVCErrorResponse. Create;
try
R. StatusCode : = AErrorCode;
if ( ( R. StatusCode div 1 0 0 ) = 2 ) then
R. ReasonString : = 'ok'
else
R. ReasonString : = 'error' ;
R. Message : = AErrorMessage;
R. Classname : = AErrorClassName;
Render( R, False , stProperties) ;
finally
R. Free;
end ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-29 14:49:35 +02:00
procedure TMVCController. Render( const ADataSet: TDataSet;
2017-03-30 17:00:04 +02:00
const AOwns: boolean ;
const AIgnoredFields: TMVCIgnoredList;
const ANameCase: TMVCNameCase;
2017-04-29 23:56:56 +02:00
const ASerializationType: TMVCDatasetSerializationType) ;
2017-03-29 14:49:35 +02:00
begin
if Assigned( ADataSet) then
begin
2017-03-30 17:00:04 +02:00
try
2017-04-29 23:56:56 +02:00
if ASerializationType = dstSingleRecord then
2017-04-14 16:43:31 +02:00
Render( Serializer( ContentType) . SerializeDataSetRecord( ADataSet, AIgnoredFields, ANameCase) )
else
Render( Serializer( ContentType) . SerializeDataSet( ADataSet, AIgnoredFields, ANameCase) )
2017-03-30 17:00:04 +02:00
finally
if AOwns then
ADataSet. Free;
end ;
2017-03-29 14:49:35 +02:00
end
else
raise EMVCException. Create( 'Can not render an empty dataset.' ) ;
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render< T> ( const ACollection: TObjectList< T> ;
const AOwns: Boolean ; const AType: TMVCSerializationType) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( ACollection) then
2015-12-02 04:14:15 +01:00
begin
2017-03-13 20:52:11 +01:00
try
Render( Serializer( ContentType) . SerializeCollection( ACollection, AType) ) ;
finally
if AOwns then
ACollection. Free;
end ;
2015-12-02 04:14:15 +01:00
end
else
2017-03-13 20:52:11 +01:00
raise EMVCException. Create( 'Can not render an empty collection.' ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-20 19:08:01 +01:00
function TMVCController. GetRenderedView( const AViewNames: TArray< string > ) : string ;
2017-04-14 16:43:31 +02:00
{$IFNDEF LINUX}
2017-03-20 19:08:01 +01:00
var
2017-04-14 16:43:31 +02:00
View: TMVCBaseViewEngine;
2017-03-20 19:08:01 +01:00
ViewName: string ;
SBuilder: TStringBuilder;
2017-04-14 16:43:31 +02:00
{$ENDIF}
2017-03-20 19:08:01 +01:00
begin
2017-04-14 16:43:31 +02:00
{$IFNDEF LINUX}
2017-03-20 19:08:01 +01:00
SBuilder : = TStringBuilder. Create;
try
try
for ViewName in AViewNames do
begin
2017-04-14 16:43:31 +02:00
View : = FEngine. ViewEngineClass. Create(
2017-03-20 19:08:01 +01:00
ViewName,
Engine,
Context,
ViewModel,
ViewDataSets,
ContentType) ;
try
View. Execute;
SBuilder. Append( View. Output) ;
finally
View. Free;
end ;
end ;
Result : = SBuilder. ToString;
except
on E: Exception do
begin
ContentType : = TMVCMediaType. TEXT_PLAIN;
Render( E) ;
end ;
end ;
finally
SBuilder. Free;
end ;
2017-04-14 16:43:31 +02:00
{$ELSE}
raise EMVCException. Create( 'Server Side Views are not supported on Linux' ) ;
{$ENDIF}
2017-03-20 19:08:01 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render< T> ( const ACollection: TObjectList< T> ) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Self. Render< T> ( ACollection, True ) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. RenderResponseStream;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Render( ResponseStream. ToString) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const ACollection: IMVCList) ;
2013-10-30 00:48:23 +01:00
begin
2017-03-13 20:52:11 +01:00
Render( ACollection, stDefault) ;
2013-10-30 00:48:23 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const ACollection: IMVCList;
const AType: TMVCSerializationType) ;
2013-11-08 23:10:25 +01:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( ACollection) then
Render( Serializer( ContentType) . SerializeCollection( TObject( ACollection) , AType) )
else
raise EMVCException. Create( 'Can not render an empty collection.' ) ;
2013-11-08 23:10:25 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const ATextWriter: TTextWriter; const AOwns: Boolean ) ;
2013-11-08 23:10:25 +01:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( ATextWriter) then
begin
try
Render( ATextWriter. ToString) ;
finally
if AOwns then
ATextWriter. Free;
end ;
end
else
raise EMVCException. Create( 'Can not render an empty textwriter.' ) ;
2013-11-08 23:10:25 +01:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const AException: Exception; AExceptionItems: TList< string > ; const AOwns: Boolean ) ;
var
S: string ;
R: TMVCErrorResponse;
I: TMVCErrorResponseItem;
2013-11-08 23:10:25 +01:00
begin
2017-03-13 20:52:11 +01:00
try
if AException is EMVCException then
ResponseStatus( EMVCException( AException) . HTTPErrorCode, AException. Message + ' [' + AException. ClassName + ']' ) ;
2013-11-08 23:10:25 +01:00
2017-03-13 20:52:11 +01:00
if ( GetContext. Response. StatusCode = HTTP_STATUS. OK) then
ResponseStatus( HTTP_STATUS. InternalServerError, AException. Message + ' [' + AException. ClassName + ']' ) ;
2015-04-01 17:01:23 +02:00
2017-03-13 20:52:11 +01:00
if ( not GetContext. Request. IsAjax) and ( GetContext. Request. ClientPrefer( TMVCMediaType. TEXT_HTML) ) then
begin
ContentType : = TMVCMediaType. TEXT_HTML;
ContentCharset : = TMVCConstants. DEFAULT_CONTENT_CHARSET;
ResponseStream. Clear;
ResponseStream. Append
( '<html><head><style>pre { color: #000000; background-color: #d0d0d0; }</style></head><body>' )
. Append( '<h1>' + Config[ TMVCConfigKey. ServerName] + ': Error Raised</h1>' )
. AppendFormat( '<pre>HTTP Return Code: %d' + sLineBreak, [ GetContext. Response. StatusCode] )
. AppendFormat( 'HTTP Reason Text: "%s"</pre>' , [ GetContext. Response. ReasonString] ) . Append( '<h3><pre>' )
. AppendFormat( 'Exception Class Name : %s' + sLineBreak, [ AException. ClassName] )
. AppendFormat( 'Exception Message : %s' + sLineBreak, [ AException. Message ] )
. Append( '</pre></h3>' ) ;
if Assigned( AExceptionItems) and ( AExceptionItems. Count > 0 ) then
begin
ResponseStream. Append( '<h2><pre>' ) ;
for S in AExceptionItems do
ResponseStream. AppendLine( '- ' + S) ;
ResponseStream. Append( '</pre><h2>' ) ;
end
else
ResponseStream. AppendLine( '<pre>No other informations available</pre>' ) ;
ResponseStream. Append( '</body></html>' ) ;
RenderResponseStream;
end
else
begin
R : = TMVCErrorResponse. Create;
try
R. StatusCode : = GetContext. Response. StatusCode;
R. ReasonString : = 'error' ;
R. Message : = AException. Message ;
R. Classname : = AException. ClassName;
if Assigned( AExceptionItems) and ( AExceptionItems. Count > 0 ) then
begin
for S in AExceptionItems do
begin
I : = TMVCErrorResponseItem. Create;
I. Message : = S;
R. Items. Add( I) ;
end ;
end ;
Render( R, False ) ;
finally
R. Free;
end ;
end ;
finally
if AOwns then
AExceptionItems. Free;
end ;
2015-04-01 17:01:23 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const AError: TMVCErrorResponse; const AOwns: Boolean ) ;
2015-04-01 17:01:23 +02:00
begin
2017-03-13 20:52:11 +01:00
if Assigned( AError) then
begin
try
Render( AError, False , stProperties) ;
finally
if AOwns then
AError. Free;
end ;
end
else
2017-05-09 23:13:51 +02:00
raise EMVCException. Create( 'Cannot render an empty error object.' ) ;
2015-04-01 17:01:23 +02:00
end ;
2017-04-29 23:56:56 +02:00
procedure TMVCController. Render( const ADataSet: TDataSet) ;
begin
Render( ADataSet, True ) ;
end ;
2017-03-30 17:00:04 +02:00
procedure TMVCController. Render( const ADataSet: TDataSet; const AOwns: boolean ) ;
2015-04-01 17:01:23 +02:00
begin
2017-04-29 23:56:56 +02:00
Render( ADataSet, AOwns, dstAllRecords) ;
2015-04-01 17:01:23 +02:00
end ;
2017-03-13 20:52:11 +01:00
procedure TMVCController. Render( const AObject: TObject) ;
2015-04-01 17:01:23 +02:00
begin
2017-03-13 20:52:11 +01:00
Render( AObject, True ) ;
2015-04-01 17:01:23 +02:00
end ;
2017-04-29 23:56:56 +02:00
procedure TMVCController. Render( const ADataSet: TDataSet; const AOwns: Boolean ; const AIgnoredFields: TMVCIgnoredList; const ASerializationType: TMVCDatasetSerializationType) ;
2015-12-02 04:14:15 +01:00
begin
2017-04-29 23:56:56 +02:00
Render( ADataSet, AOwns, [ ] , ncLowerCase, ASerializationType) ;
2015-04-01 17:01:23 +02:00
end ;
2017-04-29 23:56:56 +02:00
procedure TMVCController. Render( const ADataSet: TDataSet; const AOwns: boolean ; const ASerializationType: TMVCDatasetSerializationType) ;
2015-04-01 17:01:23 +02:00
begin
2017-04-29 23:56:56 +02:00
Render( ADataSet, AOwns, [ ] , ASerializationType) ;
2015-04-01 17:01:23 +02:00
end ;
2017-03-13 20:52:11 +01:00
{ TMVCErrorResponse }
2015-04-01 17:01:23 +02:00
2017-03-13 20:52:11 +01:00
constructor TMVCErrorResponse. Create;
2015-04-01 17:01:23 +02:00
begin
2017-03-13 20:52:11 +01:00
inherited Create;
FItems : = TObjectList< TMVCErrorResponseItem> . Create;
2015-04-01 17:01:23 +02:00
end ;
2017-05-09 23:13:51 +02:00
constructor TMVCErrorResponse. Create( AStatusCode: Integer ; AReasonString,
AMessage: String ) ;
begin
Create;
StatusCode : = AStatusCode;
ReasonString : = AReasonString;
Message : = AMessage;
end ;
2017-03-13 20:52:11 +01:00
destructor TMVCErrorResponse. Destroy;
2016-02-29 13:48:36 +01:00
begin
2017-03-13 20:52:11 +01:00
FItems. Free;
inherited Destroy;
2016-02-29 13:48:36 +01:00
end ;
2017-04-14 16:43:31 +02:00
{ TMVCBaseView }
constructor TMVCBaseViewEngine. Create(
const AViewName: string ;
const AEngine: TMVCEngine;
const AWebContext: TWebContext;
const AViewModel: TMVCViewDataObject;
const AViewDataSets: TObjectDictionary< string , TDataSet> ;
const AContentType: string ) ;
begin
inherited Create;
FViewName : = AViewName;
Engine : = AEngine;
FWebContext : = AWebContext;
FViewModel : = AViewModel;
FViewDataSets : = AViewDataSets;
FContentType : = AContentType;
FOutput : = EmptyStr;
end ;
destructor TMVCBaseViewEngine. Destroy;
begin
inherited Destroy;
end ;
function TMVCBaseViewEngine. GetRealFileName( const AViewName: string ) : string ;
var
FileName: string ;
F: string ;
DefaultViewFileExtension: string ;
begin
DefaultViewFileExtension : = Config[ TMVCConfigKey. DefaultViewFileExtension] ;
FileName : = stringReplace( AViewName, '/' , '\' , [ rfReplaceAll] ) ;
if ( FileName = '\' ) then
FileName : = '\index.' + DefaultViewFileExtension
else
FileName : = FileName + '.' + DefaultViewFileExtension;
if DirectoryExists( Config[ TMVCConfigKey. ViewPath] ) then
F : = ExpandFileName( IncludeTrailingPathDelimiter( Config. Value[ TMVCConfigKey. ViewPath] ) + FileName)
else
F : = ExpandFileName( IncludeTrailingPathDelimiter( GetApplicationFileNamePath + Config. Value[ TMVCConfigKey. ViewPath] ) + FileName) ;
if not TFile. Exists( F) then
FileName : = ExpandFileName( IncludeTrailingPathDelimiter( GetApplicationFileNamePath + Config. Value[ TMVCConfigKey. DocumentRoot] ) + FileName)
else
FileName : = F;
if FileExists( FileName) then
Result : = FileName
else
Result : = EmptyStr;
end ;
procedure TMVCBaseViewEngine. SetOutput( const AOutput: String ) ;
begin
FOutput : = AOutput;
end ;
2013-10-30 00:48:23 +01:00
initialization
_IsShuttingDown : = 0 ;
end .