delphimvcframework/sources/MVCFramework.pas

2481 lines
73 KiB
ObjectPascal
Raw Normal View History

2013-10-30 00:48:23 +01:00
unit MVCFramework;
{$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)}
{$WARNINGS OFF}
2013-10-30 00:48:23 +01:00
interface
uses
System.Generics.Collections,
MVCFramework.Logger,
Web.HTTPApp,
System.RTTI,
System.Classes,
Data.DB,
System.SysUtils,
MVCFramework.Commons,
MVCFramework.View.Cache,
IdHeaderList,
MVCFramework.ApplicationSession,
MVCFramework.Session,
StompTypes,
ObjectsMappers
2014-09-05 12:47:40 +02:00
{$IF CompilerVErsion < 27}
, Data.DBXJSON
2014-09-05 12:47:40 +02:00
{$ELSE}
, System.JSON, Web.ApacheHTTP
2014-04-16 22:52:25 +02:00
{$IFEND}
2014-09-05 12:47:40 +02:00
, ReqMulti {Delphi XE4 (all update) and XE5 (with no update) dont contains this unit. Look for the bug in QC};
2013-10-30 00:48:23 +01:00
type
2014-09-05 12:47:40 +02:00
TMVCHTTPMethodType = (httpGET, httpPOST, httpPUT, httpDELETE, httpHEAD,
httpOPTIONS, httpPATCH, httpTRACE);
2013-10-30 00:48:23 +01:00
TMVCHTTPMethods = set of TMVCHTTPMethodType;
// RTTI ATTRIBUTES
MVCHTTPMethodAttribute = class(TCustomAttribute)
private
FMVCHTTPMethods: TMVCHTTPMethods;
function GetMVCHTTPMethodsAsString: string;
public
constructor Create(AMVCHTTPMethods: TMVCHTTPMethods);
property MVCHTTPMethods: TMVCHTTPMethods read FMVCHTTPMethods;
property MVCHTTPMethodsAsString: string read GetMVCHTTPMethodsAsString;
end;
MVCBaseAttribute = class(TCustomAttribute)
end;
MVCStringAttribute = class(MVCBaseAttribute)
private
FValue: string;
public
constructor Create(const Value: string);
property Value: string read FValue;
end;
MVCConsumesAttribute = class(MVCStringAttribute)
end;
MVCProducesAttribute = class(MVCStringAttribute)
2013-11-08 23:10:25 +01:00
private
2014-05-21 17:16:15 +02:00
FProduceEncoding: string;
procedure SetProduceEncoding(const Value: string);
2013-11-08 23:10:25 +01:00
public
constructor Create(const Value: string); overload;
2014-09-05 12:47:40 +02:00
constructor Create(const Value: string;
const ProduceEncoding: string); overload;
property ProduceEncoding: string read FProduceEncoding
write SetProduceEncoding;
2013-10-30 00:48:23 +01:00
end;
MVCPathAttribute = class(MVCBaseAttribute)
private
FPath: string;
public
constructor Create(const Value: string); overload;
constructor Create; overload;
property Path: string read FPath;
end;
TMVCWebRequest = class
public
constructor Create(AWebRequest: TWebRequest); virtual;
2013-10-30 00:48:23 +01:00
private
2013-11-08 23:10:25 +01:00
FWebRequest: TWebRequest;
FPathInfo: string;
2013-10-30 00:48:23 +01:00
FParamsTable: TMVCRequestParamsTable;
FContentType: string;
FCharset: string;
2014-06-27 15:30:39 +02:00
FContentCharset: string;
2013-10-30 00:48:23 +01:00
function GetHeader(const Name: string): string;
2014-04-10 13:56:23 +02:00
function GetHeaderValue(const Name: string): string;
2013-10-30 00:48:23 +01:00
function GetPathInfo: string;
function Param(Name: string): string;
function GetParamAll(const ParamName: string): string;
function GetIsAjax: boolean;
function GetHTTPMethod: TMVCHTTPMethodType;
function GetHTTPMethodAsString: string;
function GetParamAllAsInteger(const ParamName: string): Integer;
function GetClientPreferHTML: boolean;
function GetFiles: TAbstractWebRequestFiles;
strict protected
FBodyAsJSONValue: TJSONValue;
2014-05-21 17:16:15 +02:00
FParamNames: TArray<string>;
2013-10-30 00:48:23 +01:00
public
destructor Destroy; override;
procedure SetParamsTable(AParamsTable: TMVCRequestParamsTable);
2014-05-21 17:16:15 +02:00
function GetParamNames: TArray<string>;
2013-10-30 00:48:23 +01:00
function ClientIP: string; virtual; abstract;
function ClientPrefer(MimeType: string): boolean;
function ThereIsRequestBody: boolean;
function Accept: string;
function QueryStringParam(Name: string): string; virtual;
2013-11-08 23:10:25 +01:00
function QueryStringParamExists(Name: string): boolean; virtual;
2013-10-30 00:48:23 +01:00
function ContentParam(Name: string): string; virtual;
function Cookie(Name: string): string; virtual;
property PathInfo: string read GetPathInfo;
function Body: string;
2014-05-21 17:16:15 +02:00
function BodyAs<T: class, constructor>(const RootProperty: string = ''): T;
2014-09-05 12:47:40 +02:00
function BodyAsListOf<T: class, constructor>(const RootProperty
: string = ''): TObjectList<T>;
2013-10-30 00:48:23 +01:00
function BodyAsJSONObject: TJSONObject;
function BodyAsJSONValue: TJSONValue;
2013-10-30 00:48:23 +01:00
property Headers[const HeaderName: string]: string read GetHeader;
2014-09-05 12:47:40 +02:00
property ParamsAsInteger[const ParamName: string]: Integer
read GetParamAllAsInteger;
2013-10-30 00:48:23 +01:00
property Params[const ParamName: string]: string read GetParamAll;
property IsAjax: boolean read GetIsAjax;
property HTTPMethod: TMVCHTTPMethodType read GetHTTPMethod;
property HTTPMethodAsString: string read GetHTTPMethodAsString;
property RawWebRequest: TWebRequest read FWebRequest;
property ClientPreferHTML: boolean read GetClientPreferHTML;
property Files: TAbstractWebRequestFiles read GetFiles;
2014-05-21 17:16:15 +02:00
property ContentType: string read FContentType;
2014-06-27 15:30:39 +02:00
property ContentCharset: string read FContentCharset;
2014-05-21 17:16:15 +02:00
property Charset: string read FCharset;
2013-10-30 00:48:23 +01:00
end;
{$IF CompilerVersion >= 27}
2014-10-03 11:40:57 +02:00
TMVCApacheWebRequest = class(TMVCWebRequest)
public
constructor Create(AWebRequest: TWebRequest); override;
function ClientIP: string; override;
end;
{$ENDIF}
2013-10-30 00:48:23 +01:00
TMVCISAPIWebRequest = class(TMVCWebRequest)
public
constructor Create(AWebRequest: TWebRequest); override;
function ClientIP: string; override;
end;
TMVCINDYWebRequest = class(TMVCWebRequest)
public
constructor Create(AWebRequest: TWebRequest); override;
function ClientIP: string; override;
end;
2013-11-08 23:10:25 +01:00
{$IFDEF IOCP}
2013-10-30 00:48:23 +01:00
TMVCIOCPWebRequest = class(TMVCWebRequest)
public
constructor Create(AWebRequest: TWebRequest); override;
end;
2013-11-08 23:10:25 +01:00
{$ENDIF}
2013-10-30 00:48:23 +01:00
TMVCWebResponse = class
strict private
function GetCustomHeaders: TStrings;
private
FStreamOutputDone: boolean;
2013-11-08 23:10:25 +01:00
FReasonString: string;
2013-10-30 00:48:23 +01:00
procedure SetStatusCode(const Value: Integer);
function GetStatusCode: Integer;
procedure SetReasonString(const Value: string);
function GetCookies: TCookieCollection;
procedure SetContentType(const Value: string);
function GetContentType: string;
procedure SetContent(const Value: string);
function GetContent: string;
2014-05-21 17:16:15 +02:00
function GetLocation: string;
procedure SetLocation(const Value: string);
2013-10-30 00:48:23 +01:00
property Content: string read GetContent write SetContent;
protected // do not put this as "strict"
FWebResponse: TWebResponse;
public
constructor Create(AWebResponse: TWebResponse); virtual;
destructor Destroy; override;
procedure Flush;
procedure SetCustomHeader(const Name, Value: string);
procedure SetContentStream(AStream: TStream; AContentType: string);
procedure SendHeaders;
property CustomHeaders: TStrings read GetCustomHeaders;
property StatusCode: Integer read GetStatusCode write SetStatusCode;
property ReasonString: string read FReasonString write SetReasonString;
property Cookies: TCookieCollection read GetCookies;
property ContentType: string read GetContentType write SetContentType;
2014-05-21 17:16:15 +02:00
property Location: string read GetLocation write SetLocation;
2013-10-30 00:48:23 +01:00
property RawWebResponse: TWebResponse read FWebResponse;
end;
TMVCEngine = class;
TWebContext = class
private
2013-11-08 23:10:25 +01:00
FRequest: TMVCWebRequest;
FResponse: TMVCWebResponse;
2013-10-30 00:48:23 +01:00
FParamsTable: TMVCRequestParamsTable;
2014-05-21 17:16:15 +02:00
FData: TDictionary<string, string>;
function GetData: TDictionary<string, string>;
2013-10-30 00:48:23 +01:00
protected
constructor Create(ARequest: TWebRequest; AResponse: TWebResponse); virtual;
procedure SetParams(AParamsTable: TMVCRequestParamsTable);
procedure Flush;
public
ReservedData: TObject;
destructor Destroy; override;
property Request: TMVCWebRequest read FRequest;
property Response: TMVCWebResponse read FResponse;
2014-05-21 17:16:15 +02:00
property Data: TDictionary<string, string> read GetData;
2013-10-30 00:48:23 +01:00
end;
TMVCActionProc = reference to procedure(Context: TWebContext);
TMVCBase = class(TObject)
private
2013-11-08 23:10:25 +01:00
FMVCEngine: TMVCEngine;
FMVCConfig: TMVCConfig;
2013-10-30 00:48:23 +01:00
FApplicationSession: TWebApplicationSession;
procedure SetApplicationSession(const Value: TWebApplicationSession);
protected
class function GetApplicationFileName: string;
class function GetApplicationFileNamePath: string;
public
procedure SetMVCConfig(const Value: TMVCConfig);
function GetMVCConfig: TMVCConfig;
procedure SetMVCEngine(const Value: TMVCEngine);
function GetMVCEngine: TMVCEngine;
2014-09-05 12:47:40 +02:00
property ApplicationSession: TWebApplicationSession read FApplicationSession
write SetApplicationSession;
2013-10-30 00:48:23 +01:00
end;
TMVCController = class(TMVCBase)
public
2013-11-08 23:10:25 +01:00
IsSessionStarted: boolean;
2013-10-30 00:48:23 +01:00
SessionMustBeClose: boolean;
private
2013-11-08 23:10:25 +01:00
FViewModel: TMVCDataObjects;
FViewDataSets: TObjectDictionary<string, TDataSet>;
FContext: TWebContext;
FWebSession: TWebSession;
FResponseStream: TStringBuilder;
2014-06-27 15:30:39 +02:00
FContentCharset: string;
2013-10-30 00:48:23 +01:00
procedure SetContext(const Value: TWebContext);
procedure SetWebSession(const Value: TWebSession);
procedure SetContentType(const Value: string);
function GetContentType: string;
function GetWebSession: TWebSession;
2014-06-27 15:30:39 +02:00
function GetContentCharset: string;
procedure SetContentCharset(const Value: string);
2013-10-30 00:48:23 +01:00
protected
procedure RaiseSessionExpired; virtual;
2013-11-10 01:04:17 +01:00
function GetCurrentWebModule: TWebModule;
2013-10-30 00:48:23 +01:00
function ResponseStream: TStringBuilder;
function GetNewStompClient(ClientID: string = ''): IStompClient;
function GetClientID: string;
procedure LoadView(const ViewName: string); virtual;
property Context: TWebContext read FContext write SetContext;
property Session: TWebSession read GetWebSession write SetWebSession;
procedure MVCControllerAfterCreate; virtual;
procedure MVCControllerBeforeDestroy; virtual;
property ContentType: string read GetContentType write SetContentType;
2014-09-05 12:47:40 +02:00
property ContentCharset: string read GetContentCharset
write SetContentCharset;
2013-10-30 00:48:23 +01:00
// Session
procedure SessionStart; virtual;
procedure SessionStop(ARaiseExceptionIfExpired: boolean = true); virtual;
procedure BindToSession(SessionID: string);
procedure SendSessionCookie(const SessionID: string);
// Renderers
procedure Render(const Content: string); overload; virtual;
procedure Render; overload; virtual;
2014-09-05 12:47:40 +02:00
procedure Render<T: class>(ACollection: TObjectList<T>;
AInstanceOwner: boolean = true;
2014-04-16 22:52:25 +02:00
AJSONObjectActionProc: TJSONObjectActionProc = nil); overload;
2014-09-05 12:47:40 +02:00
procedure Render(AObject: TObject; AInstanceOwner: boolean = true);
overload; virtual;
procedure Render(ADataSet: TDataSet; AInstanceOwner: boolean = false;
2014-10-26 20:48:52 +01:00
AOnlySingleRecord: boolean = false;
AJSONObjectActionProc: TJSONObjectActionProc = nil); overload; virtual;
2014-09-05 12:47:40 +02:00
procedure Render(AJSONValue: TJSONValue; AInstanceOwner: boolean = true);
2013-10-30 00:48:23 +01:00
overload; virtual;
2014-09-05 12:47:40 +02:00
procedure RenderListAsProperty<T: class>(const APropertyName: string;
AObjectList: TObjectList<T>; AOwnsInstance: boolean = true;
AJSONObjectActionProc: TJSONObjectActionProc = nil);
procedure Render(E: Exception; ErrorItems: TList<string> = nil);
overload; virtual;
2014-10-03 11:40:57 +02:00
procedure Render(const AErrorCode: UInt16; const AErrorMessage: string;
const AErrorClassName: string = ''); overload;
2013-10-30 00:48:23 +01:00
procedure Render(const AErrorCode: UInt16; AJSONValue: TJSONValue;
AInstanceOwner: boolean = true); overload;
2014-09-05 12:47:40 +02:00
procedure Render(const AErrorCode: UInt16; AObject: TObject;
AInstanceOwner: boolean = true); overload;
2014-04-15 17:03:47 +02:00
procedure RenderStreamAndFree(const AStream: TStream);
2013-10-30 00:48:23 +01:00
// messaging
2014-09-05 12:47:40 +02:00
procedure EnqueueMessageOnTopic(const ATopic: string;
AJSONObject: TJSONObject; AOwnsInstance: boolean = true);
2013-10-30 00:48:23 +01:00
function ReceiveMessageFromTopic(const ATopic: string; ATimeout: Int64;
var JSONObject: TJSONObject): boolean;
// redirects
procedure Redirect(const URL: string);
// http return code
procedure ResponseStatusCode(const ErrorCode: UInt16);
// streams and files
procedure SendStream(AStream: TStream); virtual;
procedure SendFile(AFileName: string); virtual;
// filters before, after
2014-09-05 12:47:40 +02:00
procedure OnBeforeAction(Context: TWebContext; const AActionNAme: string;
var Handled: boolean); virtual;
procedure OnAfterAction(Context: TWebContext;
const AActionNAme: string); virtual;
2013-10-30 00:48:23 +01:00
property Config: TMVCConfig read GetMVCConfig;
public
// property ViewCache: TViewCache read FViewCache write SetViewCache;
procedure PushJSONToView(const AModelName: string; AModel: TJSONValue);
procedure PushModelToView(const AModelName: string; AModel: TObject);
procedure PushDataSetToView(const AModelName: string; ADataSet: TDataSet);
constructor Create;
destructor Destroy; override;
end;
TMVCControllerClass = class of TMVCController;
IMVCMiddleware = interface
['{3278183A-124A-4214-AB4E-94CA4C22450D}']
procedure OnBeforeRouting(Context: TWebContext; var Handled: boolean);
2014-09-05 12:47:40 +02:00
procedure OnAfterControllerAction(Context: TWebContext;
const AActionNAme: string; const Handled: boolean);
end;
2013-11-08 23:10:25 +01:00
TMVCEngine = class(TComponent)
2013-10-30 00:48:23 +01:00
strict private
FApplicationSession: TWebApplicationSession;
2013-11-05 14:57:50 +01:00
2013-10-30 00:48:23 +01:00
private
2013-11-08 23:10:25 +01:00
FWebModule: TWebModule;
2013-10-30 00:48:23 +01:00
FSavedOnBeforeDispatch: THTTPMethodEvent;
2013-11-08 23:10:25 +01:00
FMVCConfig: TMVCConfig;
2013-10-30 00:48:23 +01:00
// FViewCache : TViewCache;
FMimeTypes: TDictionary<string, string>;
procedure SetApplicationSession(const Value: TWebApplicationSession);
function GetBinVersion(): string;
function GetUpTime: string;
protected
FConfiguredSessionTimeout: Int64;
2013-11-08 23:10:25 +01:00
FControllers: TList<TMVCControllerClass>;
FMiddleware: TList<IMVCMiddleware>;
2014-09-05 12:47:40 +02:00
procedure ExecuteBeforeRoutingMiddleware(Context: TWebContext;
var Handled: boolean);
procedure ExecuteAfterMiddleware(Context: TWebContext;
const AActionNAme: string; const Handled: boolean);
2014-03-08 00:00:56 +01:00
procedure ConfigDefaultValues; virtual;
2013-10-30 00:48:23 +01:00
procedure FixUpWebModule;
2014-09-05 12:47:40 +02:00
procedure OnBeforeDispatch(Sender: TObject; Request: TWebRequest;
Response: TWebResponse; var Handled: boolean); virtual;
function ExecuteAction(Sender: TObject; Request: TWebRequest;
Response: TWebResponse): boolean; virtual;
2014-03-08 00:00:56 +01:00
procedure LoadSystemControllers; virtual;
2014-09-05 12:47:40 +02:00
procedure ResponseErrorPage(E: Exception; Request: TWebRequest;
Response: TWebResponse); virtual;
function IsBuiltInMethod(const AWebRequest: TWebRequest;
const AWebResponse: TWebResponse): boolean;
procedure HandleBuiltInMethods(const AWebRequest: TWebRequest;
const AWebResponse: TWebResponse);
procedure ExecuteFile(const AFileName: string;
AContext: TWebContext); virtual;
2013-10-30 00:48:23 +01:00
public
2014-09-05 12:47:40 +02:00
class function GetCurrentSession(Config: TMVCConfig;
const AWebRequest: TWebRequest; const AWebResponse: TWebResponse;
const BindToThisSessionID: string = '';
2013-11-08 23:10:25 +01:00
ARaiseExceptionIfExpired: boolean = true): TWebSession;
constructor Create(WebModule: TWebModule); reintroduce;
2013-10-30 00:48:23 +01:00
destructor Destroy; override;
2014-09-05 12:47:40 +02:00
function AddController(AControllerClass: TMVCControllerClass)
: TMVCEngine; overload;
function AddMiddleware(AMiddleware: IMVCMiddleware): TMVCEngine;
2013-10-30 00:48:23 +01:00
// http return codes
procedure Http404(AWebContext: TWebContext);
procedure Http500(AWebContext: TWebContext; AReasonText: string = '');
property Config: TMVCConfig read FMVCConfig; // allow a simple client code
2014-09-05 12:47:40 +02:00
property ApplicationSession: TWebApplicationSession read FApplicationSession
write SetApplicationSession;
2013-10-30 00:48:23 +01:00
end;
TMVCStaticContents = class(TMVCController)
public
// [MVCPath('/static/($filename)')]
2014-09-05 12:47:40 +02:00
class procedure SendFile(AFileName, AMimeType: string;
Context: TWebContext);
class function IsStaticFile(AViewPath, AWebRequestPath: string;
out ARealFileName: string): boolean;
class function IsScriptableFile(StaticFileName: string;
Config: TMVCConfig): boolean;
2013-10-30 00:48:23 +01:00
end;
2014-03-10 17:39:29 +01:00
type
TMVCConfigKey = class
2014-04-16 22:52:25 +02:00
public const
2014-03-10 17:39:29 +01:00
SessionTimeout = 'sessiontimeout';
DocumentRoot = 'document_root';
ViewPath = 'view_path';
DefaultContentType = 'default_content_type';
2014-04-10 13:56:23 +02:00
DefaultContentCharset = 'default_content_charset';
2014-03-10 17:39:29 +01:00
DefaultViewFileExtension = 'default_view_file_extension';
ISAPIPath = 'isapi_path';
StompServer = 'stompserver';
StompServerPort = 'stompserverport';
StompUsername = 'stompusername';
StompPassword = 'stomppassword';
Messaging = 'messaging';
end;
2013-10-30 00:48:23 +01:00
function IsShuttingDown: boolean;
procedure EnterInShutdownState;
2014-09-05 12:47:40 +02:00
procedure InternalRender(const Content: string;
ContentType, ContentEncoding: string; Context: TWebContext); overload;
procedure InternalRenderText(const AContent: string;
ContentType, ContentEncoding: string; Context: TWebContext);
procedure InternalRender(AJSONValue: TJSONValue;
ContentType, ContentEncoding: string; Context: TWebContext;
2013-12-04 13:06:18 +01:00
AInstanceOwner: boolean = true); overload;
2013-10-30 00:48:23 +01:00
implementation
uses
System.SyncObjs,
idglobal,
IdGlobalProtocols,
System.DateUtils,
System.RegularExpressions,
WinApi.Windows,
System.TypInfo,
System.ioutils,
System.StrUtils,
Web.Win.IsapiHTTP,
MVCFramework.Router,
MVCFramework.View,
IdURI,
DuckListU,
IdStack
2013-11-08 23:10:25 +01:00
{$IFDEF IOCP},
2013-10-30 00:48:23 +01:00
Iocp.DSHTTPWebBroker
2013-11-05 14:57:50 +01:00
2013-11-08 23:10:25 +01:00
{$ELSE},
2013-10-30 00:48:23 +01:00
IdHTTPWebBrokerBridge
2013-11-05 14:57:50 +01:00
2013-11-08 23:10:25 +01:00
{$ENDIF},
2013-10-30 00:48:23 +01:00
LuaBind,
2014-03-06 14:20:57 +01:00
MVCFramework.BUSController, Web.WebReq;
2013-10-30 00:48:23 +01:00
type
TIdHTTPAppRequestHack = class({$IFDEF IOCP}TIocpWebRequest
{$ELSE}TIdHTTPAppRequest{$ENDIF})
2013-10-30 00:48:23 +01:00
end;
threadvar ctx: TRTTIContext;
2013-10-30 01:09:09 +01:00
2013-10-30 00:48:23 +01:00
var
_IsShuttingDown: Int64 = 0;
// this variable is used by TInterlocked functions to handl<64>e the "shuttingdown" mode
{ TMVCEngine }
2014-09-05 12:47:40 +02:00
function TMVCEngine.AddController(AControllerClass: TMVCControllerClass)
: TMVCEngine;
2013-10-30 00:48:23 +01:00
begin
FControllers.Add(AControllerClass);
Result := Self;
end;
function TMVCEngine.AddMiddleware(AMiddleware: IMVCMiddleware): TMVCEngine;
begin
FMiddleware.Add(AMiddleware);
Result := Self;
end;
2013-10-30 00:48:23 +01:00
procedure TMVCEngine.ConfigDefaultValues;
begin
2014-03-10 17:39:29 +01:00
Config[TMVCConfigKey.SessionTimeout] := '30'; // 30 minutes
2014-04-10 13:56:23 +02:00
Config[TMVCConfigKey.DocumentRoot] := '.\www';
2014-03-10 17:39:29 +01:00
Config[TMVCConfigKey.ViewPath] := 'eLua';
2014-09-05 12:47:40 +02:00
Config[TMVCConfigKey.DefaultContentType] :=
TMVCConstants.DEFAULT_CONTENT_TYPE;
Config[TMVCConfigKey.DefaultContentCharset] :=
TMVCConstants.DEFAULT_CONTENT_CHARSET;
2014-04-10 13:56:23 +02:00
2014-03-10 17:39:29 +01:00
Config[TMVCConfigKey.DefaultViewFileExtension] := 'elua';
Config[TMVCConfigKey.ISAPIPath] := '';
Config[TMVCConfigKey.StompServer] := 'localhost';
Config[TMVCConfigKey.StompServerPort] := '61613';
Config[TMVCConfigKey.StompUsername] := 'guest';
Config[TMVCConfigKey.StompPassword] := 'guest';
2014-04-10 13:56:23 +02:00
Config[TMVCConfigKey.Messaging] := 'false';
2014-03-10 17:39:29 +01:00
// Config['sessiontimeout'] := '30'; // 30 minutes
// Config['document_root'] := '..\..\..\www';
// Config['view_path'] := 'eLua';
// Config['default_content_type'] := TMVCMimeType.APPLICATION_JSON;
// Config['default_view_file_extension'] := 'elua';
// Config['isapi_path'] := '';
//
// Config['stompserver'] := 'localhost';
// Config['stompserverport'] := '61613';
// Config['stompusername'] := 'guest';
// Config['stomppassword'] := 'guest';
// Config['messaging'] := 'true';
2013-10-30 00:48:23 +01:00
/// ///////
FMimeTypes.Add('.html', TMVCMimeType.TEXT_HTML);
FMimeTypes.Add('.htm', TMVCMimeType.TEXT_HTML);
FMimeTypes.Add('.txt', TMVCMimeType.TEXT_PLAIN);
FMimeTypes.Add('.css', TMVCMimeType.TEXT_CSS);
FMimeTypes.Add('.js', TMVCMimeType.TEXT_JAVASCRIPT);
FMimeTypes.Add('.jpg', TMVCMimeType.IMAGE_JPEG);
FMimeTypes.Add('.jpeg', TMVCMimeType.IMAGE_JPEG);
FMimeTypes.Add('.png', TMVCMimeType.IMAGE_PNG);
FMimeTypes.Add('.appcache', TMVCMimeType.TEXT_CACHEMANIFEST);
end;
constructor TMVCEngine.Create(WebModule: TWebModule);
begin
2013-11-08 23:10:25 +01:00
inherited Create(WebModule);
2014-03-06 14:20:57 +01:00
WebRequestHandler.CacheConnections := true;
WebRequestHandler.MaxConnections := 1024;
2013-10-30 00:48:23 +01:00
FMimeTypes := TDictionary<string, string>.Create;
FMVCConfig := TMVCConfig.Create;
FWebModule := WebModule;
FControllers := TList<TMVCControllerClass>.Create;
FMiddleware := TList<IMVCMiddleware>.Create;
2013-10-30 00:48:23 +01:00
// FViewCache := TViewCache.Create;
FixUpWebModule;
ConfigDefaultValues;
LoadSystemControllers;
end;
destructor TMVCEngine.Destroy;
begin
FMimeTypes.Free;
FMVCConfig.Free;
FControllers.Free;
FMiddleware.Free;
2013-10-30 00:48:23 +01:00
// FViewCache.Free;
inherited;
end;
2014-09-05 12:47:40 +02:00
function TMVCEngine.ExecuteAction(Sender: TObject; Request: TWebRequest;
Response: TWebResponse): boolean;
2013-10-30 00:48:23 +01:00
var
SelectedController: TMVCController;
2013-11-08 23:10:25 +01:00
Context: TWebContext;
ParamsTable: TMVCRequestParamsTable;
Router: TMVCRouter;
StaticFileName: string;
ContentType: string;
Handled: boolean;
2014-06-27 15:30:39 +02:00
ResponseContentType, ResponseContentCharset: string;
2013-10-30 00:48:23 +01:00
begin
LogEnterMethod(Request.PathInfo);
try
2013-11-10 01:04:17 +01:00
Result := false;
2013-10-30 00:48:23 +01:00
ParamsTable := TMVCRequestParamsTable.Create;
try
Context := TWebContext.Create(Request, Response);
try
// Static file handling
2014-09-05 12:47:40 +02:00
if TMVCStaticContents.IsStaticFile(TPath.Combine(AppPath,
FMVCConfig[TMVCConfigKey.DocumentRoot]), Request.PathInfo,
StaticFileName) then
2013-10-30 00:48:23 +01:00
begin
2014-09-05 12:47:40 +02:00
if TMVCStaticContents.IsScriptableFile(StaticFileName, FMVCConfig)
then
2013-10-30 00:48:23 +01:00
// execute the file
begin
ExecuteFile(StaticFileName, Context);
end
else // serve the file
begin
2014-09-05 12:47:40 +02:00
if not FMimeTypes.TryGetValue
(LowerCase(ExtractFileExt(StaticFileName)), ContentType) then
2013-10-30 00:48:23 +01:00
ContentType := TMVCMimeType.APPLICATION_OCTETSTREAM;
TMVCStaticContents.SendFile(StaticFileName, ContentType, Context);
end;
end
else
begin
Router := TMVCRouter.Create(Config);
try
ExecuteBeforeRoutingMiddleware(Context, Handled);
if not Handled then
2013-10-30 00:48:23 +01:00
begin
2014-09-05 12:47:40 +02:00
if Router.ExecuteRouting(Request.PathInfo,
2014-06-27 15:30:39 +02:00
TMVCRouter.StringMethodToHTTPMetod(Request.Method),
2014-09-05 12:47:40 +02:00
Request.ContentType, Request.Accept, FControllers,
2014-06-27 15:30:39 +02:00
FMVCConfig[TMVCConfigKey.DefaultContentType],
2014-09-05 12:47:40 +02:00
FMVCConfig[TMVCConfigKey.DefaultContentCharset], ParamsTable,
ResponseContentType, ResponseContentCharset) then
begin
SelectedController := Router.MVCControllerClass.Create;
2013-10-30 00:48:23 +01:00
try
SelectedController.SetMVCConfig(Config);
SelectedController.ApplicationSession := FApplicationSession;
Context.SetParams(ParamsTable);
SelectedController.SetContext(Context);
SelectedController.SetMVCEngine(Self);
2014-09-05 12:47:40 +02:00
Log(TLogLevel.levNormal, Context.Request.HTTPMethodAsString +
':' + Request.RawPathInfo + ' -> ' +
Router.MVCControllerClass.QualifiedClassName);
// exception?
2013-12-05 15:00:11 +01:00
try
SelectedController.MVCControllerAfterCreate;
try
Handled := false;
// gets response contentype from MVCProduces attribute
2014-04-01 00:02:31 +02:00
SelectedController.ContentType := ResponseContentType;
2014-09-05 12:47:40 +02:00
SelectedController.ContentCharset :=
ResponseContentCharset;
SelectedController.OnBeforeAction(Context,
Router.MethodToCall.Name, Handled);
if not Handled then
2013-12-05 15:00:11 +01:00
begin
2014-04-01 00:02:31 +02:00
try
2014-09-05 12:47:40 +02:00
Router.MethodToCall.Invoke(SelectedController,
[Context]);
2014-04-01 00:02:31 +02:00
finally
2014-09-05 12:47:40 +02:00
SelectedController.OnAfterAction(Context,
Router.MethodToCall.Name);
end;
end;
2013-12-05 15:00:11 +01:00
if SelectedController.SessionMustBeClose then
begin
// SessionList.Remove(SelectedController.Session.SessionID);
end
else
begin
end;
finally
SelectedController.MVCControllerBeforeDestroy;
end;
2014-09-05 12:47:40 +02:00
ExecuteAfterMiddleware(Context,
Router.MethodToCall.Name, Handled);
except
on E: EMVCSessionExpiredException do
2013-10-30 00:48:23 +01:00
begin
LogException(E, E.DetailedMessage);
SelectedController.SessionStop(false);
SelectedController.ResponseStatusCode(E.HTTPErrorCode);
SelectedController.Render(E);
end;
on E: EMVCException do
2013-12-05 15:00:11 +01:00
begin
LogException(E, E.DetailedMessage);
SelectedController.ResponseStatusCode(E.HTTPErrorCode);
SelectedController.Render(E);
end;
2014-04-01 00:02:31 +02:00
on E: EInvalidOp do
begin
LogException(E, 'Invalid OP');
SelectedController.Render(E);
end;
on E: Exception do
begin
2014-04-01 00:02:31 +02:00
LogException(E, 'Global Action Exception Handler');
SelectedController.Render(E);
2013-12-05 15:00:11 +01:00
end;
2013-10-30 00:48:23 +01:00
end;
2014-09-05 12:47:40 +02:00
Context.Response.ContentType :=
SelectedController.ContentType;
finally
SelectedController.Free;
2013-10-30 00:48:23 +01:00
end;
end
else if IsBuiltInMethod(Request, Response) then
begin
HandleBuiltInMethods(Request, Response);
end
else
begin
Http404(Context);
2013-10-30 00:48:23 +01:00
end;
end;
2013-10-30 00:48:23 +01:00
finally
Router.Free;
end;
end; // end if IS_STATIC
finally
Context.Free;
end;
finally
ParamsTable.Free;
end;
finally
2014-09-05 12:47:40 +02:00
LogExitMethod(Request.PathInfo + ' [' + IntToStr(Response.StatusCode) + ' '
+ Response.ReasonString + ']');
2013-10-30 00:48:23 +01:00
end;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCEngine.ExecuteAfterMiddleware(Context: TWebContext;
const AActionNAme: string; const Handled: boolean);
var
middleware: IMVCMiddleware;
begin
for middleware in FMiddleware do
begin
middleware.OnAfterControllerAction(Context, AActionNAme, Handled);
end;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCEngine.ExecuteBeforeRoutingMiddleware(Context: TWebContext;
var Handled: boolean);
var
middleware: IMVCMiddleware;
begin
if not Handled then
for middleware in FMiddleware do
begin
middleware.OnBeforeRouting(Context, Handled);
if Handled then
break;
end;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCEngine.ExecuteFile(const AFileName: string;
AContext: TWebContext);
2013-10-30 00:48:23 +01:00
var
View: TMVCEmbeddedLuaView;
begin
try
View := TMVCEmbeddedLuaView.Create('', Self, AContext, nil, nil, '');
try
View.FileName := AFileName;
View.SetMVCConfig(FMVCConfig);
View.Execute;
AContext.Response.Content := View.Output;
finally
View.Free;
end;
except
on E: Exception do
begin
AContext.Response.ContentType := TMVCMimeType.TEXT_PLAIN;
AContext.Response.Content := E.ClassName + ' ' + E.Message;
AContext.Response.StatusCode := 500;
end;
end;
end;
procedure TMVCEngine.FixUpWebModule;
begin
FSavedOnBeforeDispatch := FWebModule.BeforeDispatch;
FWebModule.BeforeDispatch := OnBeforeDispatch;
end;
function TMVCEngine.GetBinVersion: string;
begin
raise Exception.Create('Not implemented');
end;
2014-09-05 12:47:40 +02:00
class function TMVCEngine.GetCurrentSession(Config: TMVCConfig;
const AWebRequest: TWebRequest; const AWebResponse: TWebResponse;
const BindToThisSessionID: string; ARaiseExceptionIfExpired: boolean)
2013-10-30 00:48:23 +01:00
: TWebSession;
var
SessionID: string;
2013-11-08 23:10:25 +01:00
List: TObjectDictionary<string, TWebSession>;
2013-10-30 00:48:23 +01:00
IsExpired: boolean;
begin
List := SessionList;
TMonitor.Enter(List);
try
Result := nil;
if BindToThisSessionID.IsEmpty then
begin
2014-09-05 12:47:40 +02:00
SessionID := AWebRequest.CookieFields.Values
[TMVCConstants.SESSION_TOKEN_NAME];
2013-10-30 00:48:23 +01:00
if not SessionID.IsEmpty then
SessionID := TIdURI.URLDecode(SessionID);
end
else
begin
SessionID := BindToThisSessionID;
end;
{ SESSION IS NOT AUTOCREATED BY DEFAULT }
if not SessionID.IsEmpty then
begin
IsExpired := true;
if List.TryGetValue(SessionID, Result) then
begin
2014-09-05 12:47:40 +02:00
IsExpired := MinutesBetween(now, Result.LastAccess) >
StrToInt(Config.Value['sessiontimeout']);
2013-10-30 00:48:23 +01:00
end;
if Assigned(Result) then
begin
if IsExpired then
begin
List.Remove(SessionID); // remove expired session from session list
if ARaiseExceptionIfExpired then
raise EMVCSessionExpiredException.Create('Session expired')
else
Result := nil;
end
else
begin
Result.MarkAsUsed;
end;
end;
end;
finally
TMonitor.Exit(List);
end;
end;
function MSecToTime(mSec: Int64): string;
const
secondTicks = 1000;
minuteTicks = 1000 * 60;
hourTicks = 1000 * 60 * 60;
dayTicks = 1000 * 60 * 60 * 24;
var
2013-11-08 23:10:25 +01:00
D, H, M, S: string;
2013-10-30 00:48:23 +01:00
ZD, ZH, ZM, ZS: Integer;
begin
ZD := mSec div dayTicks;
Dec(mSec, ZD * dayTicks);
ZH := mSec div hourTicks;
Dec(mSec, ZH * hourTicks);
ZM := mSec div minuteTicks;
Dec(mSec, ZM * minuteTicks);
ZS := mSec div secondTicks;
D := IntToStr(ZD);
H := IntToStr(ZH);
M := IntToStr(ZM);
S := IntToStr(ZS);
Result := D + '.' + H + ':' + M + ':' + S;
end;
function TMVCEngine.GetUpTime: string;
begin
Result := MSecToTime(GetTickCount);
end;
2014-09-05 12:47:40 +02:00
procedure TMVCEngine.HandleBuiltInMethods(const AWebRequest: TWebRequest;
const AWebResponse: TWebResponse);
2013-10-30 00:48:23 +01:00
var
2013-11-08 23:10:25 +01:00
j: TJSONObject;
c: TMVCControllerClass;
_type: TRttiInstanceType;
2014-04-01 00:02:31 +02:00
_method: TRttiMethod;
_methods: TArray<TRttiMethod>;
2013-11-08 23:10:25 +01:00
ControllerInfo: TJSONObject;
jmethod: TJSONObject;
_a: TCustomAttribute;
methods: TJSONArray;
FoundAttrib: boolean;
2013-10-30 00:48:23 +01:00
StrRelativePath: string;
2013-11-08 23:10:25 +01:00
StrHTTPMethods: string;
StrConsumes: string;
StrProduces: string;
2013-10-30 00:48:23 +01:00
begin
{ TODO -oDaniele -cGeneral : Please! Use a register here!! }
2013-10-30 00:48:23 +01:00
if LowerCase(string(AWebRequest.PathInfo)) = '/describeserver.info' then
begin
j := TJSONObject.Create;
try
for c in FControllers do
begin
ControllerInfo := TJSONObject.Create;
j.AddPair(c.QualifiedClassName, ControllerInfo);
_type := ctx.GetType(c) as TRttiInstanceType;
for _a in _type.GetAttributes do
begin
if _a is MVCPathAttribute then
ControllerInfo.AddPair('ResourcePath', MVCPathAttribute(_a).Path)
end;
methods := TJSONArray.Create;
ControllerInfo.AddPair('Actions', methods);
_methods := _type.GetDeclaredMethods;
for _method in _methods do
begin
2013-11-10 01:04:17 +01:00
FoundAttrib := false;
2013-10-30 00:48:23 +01:00
StrRelativePath := '';
StrHTTPMethods := '';
StrConsumes := '';
StrProduces := '';
2013-10-30 00:48:23 +01:00
for _a in _method.GetAttributes do
begin
if _a is MVCPathAttribute then
begin
StrRelativePath := MVCPathAttribute(_a).Path;
FoundAttrib := true;
end;
if _a is MVCHTTPMethodAttribute then
begin
2014-09-05 12:47:40 +02:00
StrHTTPMethods := MVCHTTPMethodAttribute(_a)
.MVCHTTPMethodsAsString;
FoundAttrib := true;
end;
if _a is MVCConsumesAttribute then
begin
StrConsumes := MVCConsumesAttribute(_a).Value;
FoundAttrib := true;
end;
if _a is MVCProducesAttribute then
begin
StrProduces := MVCProducesAttribute(_a).Value;
2013-10-30 00:48:23 +01:00
FoundAttrib := true;
end;
end;
if FoundAttrib then
begin
jmethod := TJSONObject.Create;
jmethod.AddPair('ActionName', _method.Name);
jmethod.AddPair('RelativePath', StrRelativePath);
jmethod.AddPair('Consumes', StrConsumes);
jmethod.AddPair('Produces', StrProduces);
2013-10-30 00:48:23 +01:00
jmethod.AddPair('HTTPMethods', StrHTTPMethods);
methods.AddElement(jmethod);
end;
end;
end;
AWebResponse.ContentType := TMVCMimeType.APPLICATION_JSON;
AWebResponse.Content := j.ToString;
AWebResponse.StatusCode := 200;
finally
j.Free;
end;
end
2014-09-05 12:47:40 +02:00
else if LowerCase(string(AWebRequest.PathInfo)) = '/describeplatform.info'
then
2013-10-30 00:48:23 +01:00
begin
j := TJSONObject.Create;
try
j.AddPair('os', TOSVersion.ToString);
2013-11-08 23:10:25 +01:00
// j.AddPair('binversion', GetBinVersion());
2013-10-30 00:48:23 +01:00
j.AddPair('CPUs', TJSONNumber.Create(TThread.ProcessorCount));
j.AddPair('CPU_architecture', IntToStr(Ord(TOSVersion.Architecture)) +
' /*(0=Intelx86; 1=Intelx64 2=ARM32)*/');
j.AddPair('uptime', GetUpTime);
2013-11-08 23:10:25 +01:00
2013-10-30 00:48:23 +01:00
AWebResponse.ContentType := TMVCMimeType.APPLICATION_JSON;
AWebResponse.Content := j.ToString;
AWebResponse.StatusCode := 200;
finally
j.Free;
end;
end
else if LowerCase(string(AWebRequest.PathInfo)) = '/serverconfig.info' then
begin
2013-11-08 23:10:25 +01:00
AWebResponse.ContentType := TMVCMimeType.APPLICATION_JSON;
AWebResponse.Content := Config.ToString.Replace('\', '\\', [rfReplaceAll]);
2013-10-30 00:48:23 +01:00
AWebResponse.StatusCode := 200;
end;
end;
procedure TMVCEngine.Http404(AWebContext: TWebContext);
begin
AWebContext.Response.StatusCode := 404;
AWebContext.Response.ReasonString := 'Not Found';
AWebContext.Response.Content := 'Not Found';
end;
procedure TMVCEngine.Http500(AWebContext: TWebContext; AReasonText: string);
begin
AWebContext.Response.StatusCode := 500;
AWebContext.Response.ReasonString := 'Internal server error: ' + AReasonText;
AWebContext.Response.Content := 'Internal server error: ' + AReasonText;
end;
2014-09-05 12:47:40 +02:00
function TMVCEngine.IsBuiltInMethod(const AWebRequest: TWebRequest;
const AWebResponse: TWebResponse): boolean;
2013-10-30 00:48:23 +01:00
begin
Result := (LowerCase(AWebRequest.PathInfo) = '/describeserver.info') or
(LowerCase(AWebRequest.PathInfo) = '/describeplatform.info') or
(LowerCase(AWebRequest.PathInfo) = '/serverconfig.info');
end;
procedure TMVCEngine.LoadSystemControllers;
begin
// AddController(TMVCStaticContents); //--daniele Static files are not handled directly by the router
AddController(TMVCBUSController);
end;
2014-09-05 12:47:40 +02:00
procedure TMVCEngine.OnBeforeDispatch(Sender: TObject; Request: TWebRequest;
Response: TWebResponse; var Handled: boolean);
2013-10-30 00:48:23 +01:00
begin
2013-11-10 01:04:17 +01:00
Handled := false;
2013-10-30 00:48:23 +01:00
if Assigned(FSavedOnBeforeDispatch) then
FSavedOnBeforeDispatch(Sender, Request, Response, Handled);
// _Request := Request as TIdHTTPAppRequest;
if not Handled then
begin
try
// "X-Requested-With", "XMLHttpRequest"
ExecuteAction(Sender, Request, Response);
except
on E: Exception do
begin
LogException(E);
// Response.ContentStream.Size := 0;
Response.Content := E.Message;
Response.SendResponse;
end;
end;
Handled := true;
end;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCEngine.ResponseErrorPage(E: Exception; Request: TWebRequest;
Response: TWebResponse);
2013-10-30 00:48:23 +01:00
begin
Response.SetCustomHeader('x-mvc-error', E.ClassName + ': ' + E.Message);
Response.StatusCode := 200;
if Pos('text/html', LowerCase(Request.Accept)) = 1 then
begin
Response.ContentType := 'text/plain';
2014-09-05 12:47:40 +02:00
Response.Content := 'DelphiMVCFramework ERROR:' + sLineBreak +
'Exception raised of class: ' + E.ClassName + sLineBreak +
'***********************************************' + sLineBreak + E.Message
+ sLineBreak + '***********************************************';
2013-10-30 00:48:23 +01:00
end
else
begin
Response.ContentType := 'text/plain';
2014-09-05 12:47:40 +02:00
Response.Content := 'DelphiMVCFramework ERROR:' + sLineBreak +
'Exception raised of class: ' + E.ClassName + sLineBreak +
'***********************************************' + sLineBreak + E.Message
+ sLineBreak + '***********************************************';
2013-10-30 00:48:23 +01:00
end;
end;
procedure TMVCEngine.SetApplicationSession(const Value: TWebApplicationSession);
begin
FApplicationSession := Value;
end;
{ TWebContext }
constructor TWebContext.Create(ARequest: TWebRequest; AResponse: TWebResponse);
begin
inherited Create;
2013-10-30 00:48:23 +01:00
if IsLibrary then
begin
{$IF CompilerVersion >= 27}
2014-05-30 11:29:58 +02:00
if ARequest is TApacheRequest then
FRequest := TMVCApacheWebRequest.Create(ARequest)
else if ARequest is TISAPIRequest then
FRequest := TMVCISAPIWebRequest.Create(ARequest)
else
2014-05-30 11:29:58 +02:00
raise EMVCException.Create('Unknown request type ' + ARequest.ClassName);
{$ELSE}
FRequest := TMVCISAPIWebRequest.Create(ARequest)
{$ENDIF}
2013-10-30 00:48:23 +01:00
end
else
begin
2013-11-08 23:10:25 +01:00
{$IFDEF IOCP}
2013-10-30 00:48:23 +01:00
FRequest := TMVCIOCPWebRequest.Create(ARequest);
2013-11-08 23:10:25 +01:00
{$ELSE}
2013-10-30 00:48:23 +01:00
FRequest := TMVCINDYWebRequest.Create(ARequest);
2013-11-08 23:10:25 +01:00
{$ENDIF}
2013-10-30 00:48:23 +01:00
end;
FResponse := TMVCWebResponse.Create(AResponse);
2014-05-21 17:16:15 +02:00
FData := TDictionary<string, string>.Create;
2013-10-30 00:48:23 +01:00
end;
destructor TWebContext.Destroy;
begin
FreeAndNil(FResponse);
FreeAndNil(FRequest);
FreeAndNil(FData);
2013-10-30 00:48:23 +01:00
inherited;
end;
procedure TWebContext.Flush;
begin
FResponse.Flush;
end;
2014-05-21 17:16:15 +02:00
function TWebContext.GetData: TDictionary<string, string>;
begin
Result := FData;
end;
2013-10-30 00:48:23 +01:00
procedure TWebContext.SetParams(AParamsTable: TMVCRequestParamsTable);
begin
FParamsTable := AParamsTable;
FRequest.FParamsTable := AParamsTable;
end;
{ TMVCWebResponse }
constructor TMVCWebResponse.Create(AWebResponse: TWebResponse);
begin
2013-11-10 01:04:17 +01:00
FStreamOutputDone := false;
2013-10-30 00:48:23 +01:00
inherited Create;
FWebResponse := AWebResponse;
end;
destructor TMVCWebResponse.Destroy;
begin
Flush;
inherited;
end;
procedure TMVCWebResponse.Flush;
begin
try
FWebResponse.SendResponse; // daniele
2013-10-30 00:48:23 +01:00
except
end;
end;
function TMVCWebResponse.GetContent: string;
begin
Result := FWebResponse.Content;
end;
function TMVCWebResponse.GetContentType: string;
begin
Result := FWebResponse.ContentType;
end;
function TMVCWebResponse.GetCookies: TCookieCollection;
begin
Result := Self.FWebResponse.Cookies;
end;
function TMVCWebResponse.GetCustomHeaders: TStrings;
begin
Result := FWebResponse.CustomHeaders;
end;
2014-05-21 17:16:15 +02:00
function TMVCWebResponse.GetLocation: string;
2014-03-25 12:41:23 +01:00
begin
Result := CustomHeaders.Values['location'];
end;
2013-10-30 00:48:23 +01:00
function TMVCWebResponse.GetStatusCode: Integer;
begin
Result := FWebResponse.StatusCode;
end;
procedure TMVCWebResponse.SendHeaders;
begin
FWebResponse.SendResponse
end;
procedure TMVCWebResponse.SetContent(const Value: string);
begin
FWebResponse.Content := Value;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCWebResponse.SetContentStream(AStream: TStream;
AContentType: string);
2013-10-30 00:48:23 +01:00
begin
FWebResponse.ContentType := AContentType;
FWebResponse.ContentStream := AStream;
end;
procedure TMVCWebResponse.SetContentType(const Value: string);
begin
FWebResponse.ContentType := Value;
end;
procedure TMVCWebResponse.SetCustomHeader(const Name, Value: string);
begin
Self.FWebResponse.SetCustomHeader(name, Value);
end;
2014-05-21 17:16:15 +02:00
procedure TMVCWebResponse.SetLocation(const Value: string);
2014-03-25 12:41:23 +01:00
begin
CustomHeaders.Values['location'] := Value;
end;
2013-10-30 00:48:23 +01:00
procedure TMVCWebResponse.SetReasonString(const Value: string);
begin
FReasonString := Value;
end;
procedure TMVCWebResponse.SetStatusCode(const Value: Integer);
begin
FWebResponse.StatusCode := Value;
end;
{ TMVCWebRequest }
function TMVCWebRequest.Accept: string;
begin
Result := Self.FWebRequest.Accept;
end;
function TMVCWebRequest.Body: string;
var
InEnc: TEncoding;
Buffer: TArray<Byte>;
I: Integer;
2013-10-30 00:48:23 +01:00
begin
2014-09-05 12:47:40 +02:00
{$IF CompilerVersion > 27}
Exit(FWebRequest.Content);
{$ELSE}
// Property FWebRequest.Content is broken. It doesn't correctly decode the response body
// considering the content charser. So, here's the fix
// check http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx
if FCharset.IsEmpty then
begin
SetLength(Buffer, 10);
for I := 0 to 9 do
begin
Buffer[I] := Byte(FWebRequest.RawContent[I]);
end;
TEncoding.GetBufferEncoding(Buffer, InEnc, TEncoding.Default);
SetLength(Buffer, 0);
end
else
InEnc := TEncoding.GetEncoding(FCharset);
2014-05-21 17:16:15 +02:00
try
2014-09-05 12:47:40 +02:00
Buffer := TEncoding.Convert(InEnc, TEncoding.Default,
TBytes(FWebRequest.RawContent));
2014-05-21 17:16:15 +02:00
Result := TEncoding.Default.GetString(Buffer);
finally
InEnc.Free;
end
2014-09-05 12:47:40 +02:00
{$ENDIF}
2013-10-30 00:48:23 +01:00
end;
2014-05-21 17:16:15 +02:00
function TMVCWebRequest.BodyAs<T>(const RootProperty: string): T;
var
S: string;
JObj: TJSONObject;
begin
if ContentType.Equals(TMVCMimeType.APPLICATION_JSON) then
begin
if RootProperty = '' then
begin
JObj := BodyAsJSONObject;
if not Assigned(JObj) then
raise EMVCException.Create('Invalid or not present JSON body');
Result := Mapper.JSONObjectToObject<T>(JObj);
end
else
begin
S := Mapper.GetStringDef(BodyAsJSONObject, RootProperty, '');
if not S.IsEmpty then
2014-09-05 12:47:40 +02:00
Result := Mapper.JSONObjectToObject<T>(BodyAsJSONObject.Get(S)
.JsonValue as TJSONObject)
else
2014-09-05 12:47:40 +02:00
raise EMVCException.CreateFmt('Body property %s not valid',
[RootProperty]);
end;
end
else
2014-09-05 12:47:40 +02:00
raise EMVCException.CreateFmt('Body ContentType %s not supported',
[ContentType]);
end;
2013-10-30 00:48:23 +01:00
function TMVCWebRequest.BodyAsJSONObject: TJSONObject;
begin
Result := BodyAsJSONValue as TJSONObject;
end;
function TMVCWebRequest.BodyAsJSONValue: TJSONValue;
begin
if not Assigned(FBodyAsJSONValue) then
2013-10-30 00:48:23 +01:00
try
FBodyAsJSONValue := TJSONObject.ParseJSONValue(Body);
2013-10-30 00:48:23 +01:00
except
FBodyAsJSONValue := nil;
2013-10-30 00:48:23 +01:00
end;
Result := FBodyAsJSONValue;
2013-10-30 00:48:23 +01:00
end;
2014-09-05 12:47:40 +02:00
function TMVCWebRequest.BodyAsListOf<T>(const RootProperty: string)
: TObjectList<T>;
var
S: string;
begin
if ContentType.Equals(TMVCMimeType.APPLICATION_JSON) then
begin
if RootProperty = '' then
Result := Mapper.JSONArrayToObjectList<T>(BodyAsJSONValue as TJSONArray)
else
begin
S := Mapper.GetStringDef(BodyAsJSONObject, RootProperty, '');
if not S.IsEmpty then
2014-09-05 12:47:40 +02:00
Result := Mapper.JSONArrayToObjectList<T>(BodyAsJSONObject.Get(S)
.JsonValue as TJSONArray)
else
2014-09-05 12:47:40 +02:00
raise EMVCException.CreateFmt('Body property %s not valid',
[RootProperty]);
end;
end
else
2014-09-05 12:47:40 +02:00
raise EMVCException.CreateFmt('Body ContentType %s not supported',
[ContentType]);
end;
2013-10-30 00:48:23 +01:00
function TMVCWebRequest.ClientPrefer(MimeType: string): boolean;
begin
Result := AnsiPos(MimeType, LowerCase(RawWebRequest.Accept)) = 1;
end;
function TMVCWebRequest.ContentParam(Name: string): string;
begin
Result := FWebRequest.ContentFields.Values[name];
end;
function TMVCWebRequest.Cookie(Name: string): string;
begin
Result := FWebRequest.CookieFields.Values[name];
end;
constructor TMVCWebRequest.Create(AWebRequest: TWebRequest);
var
CT: TArray<string>;
2014-05-21 17:16:15 +02:00
c: string;
begin
inherited Create;
2013-11-18 11:51:59 +01:00
c := AWebRequest.GetFieldByName('Content-Type');
if not c.IsEmpty then
begin
2013-11-18 11:51:59 +01:00
CT := c.Split([';']);
FContentType := trim(CT[0]);
2014-06-27 15:30:39 +02:00
FCharset := TMVCConstants.DEFAULT_CONTENT_CHARSET; // default charset
2013-11-18 11:51:59 +01:00
if Length(CT) > 1 then
begin
2013-11-18 11:51:59 +01:00
if CT[1].trim.StartsWith('charset', true) then
begin
FCharset := CT[1].trim.Split(['='])[1].trim;
2013-11-18 11:51:59 +01:00
end;
end;
end;
2014-04-10 13:56:23 +02:00
2014-06-27 15:30:39 +02:00
// c := GetHeaderValue('content-encoding');
// if c.IsEmpty then
// FContentEncoding := c;
end;
2013-10-30 00:48:23 +01:00
destructor TMVCWebRequest.Destroy;
begin
FreeAndNil(FBodyAsJSONValue);
2013-10-30 00:48:23 +01:00
inherited;
end;
{ TMVCAction }
procedure TMVCController.BindToSession(SessionID: string);
begin
if not Assigned(FWebSession) then
begin
2014-09-05 12:47:40 +02:00
FWebSession := TMVCEngine.GetCurrentSession(GetMVCConfig,
Context.Request.FWebRequest, Context.Response.FWebResponse,
SessionID, false);
2013-10-30 00:48:23 +01:00
if not Assigned(FWebSession) then
raise EMVCException.Create('Invalid SessionID');
FWebSession.MarkAsUsed;
SendSessionCookie(SessionID);
end
else
raise EMVCException.Create('Session already bounded for this request');
end;
constructor TMVCController.Create;
begin
inherited Create;
2013-11-10 01:04:17 +01:00
IsSessionStarted := false;
SessionMustBeClose := false;
2014-06-27 15:30:39 +02:00
FContentCharset := TMVCConstants.DEFAULT_CONTENT_CHARSET;
2013-10-30 00:48:23 +01:00
end;
destructor TMVCController.Destroy;
begin
FreeAndNil(FResponseStream);
2014-04-10 13:56:23 +02:00
FreeAndNil(FViewDataSets);
FreeAndNil(FViewModel);
2013-10-30 00:48:23 +01:00
inherited;
end;
function TMVCController.GetClientID: string;
begin
if Session['username'].IsEmpty then
2014-09-05 12:47:40 +02:00
raise EMVCException.Create
('Messaging extensions require a valid "username" key in session');
2013-10-30 00:48:23 +01:00
Result := Session['username'];
// + IntToStr(GetTickCount);
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.EnqueueMessageOnTopic(const ATopic: string;
AJSONObject: TJSONObject; AOwnsInstance: boolean);
2013-10-30 00:48:23 +01:00
var
Stomp: IStompClient;
2013-11-08 23:10:25 +01:00
H: IStompHeaders;
msg: TJSONObject;
2013-10-30 00:48:23 +01:00
begin
msg := TJSONObject.Create;
try
if AOwnsInstance then
msg.AddPair('message', AJSONObject)
else
msg.AddPair('message', AJSONObject.Clone as TJSONObject);
msg.AddPair('_topic', ATopic).AddPair('_username', GetClientID)
.AddPair('_timestamp', FormatDateTime('YYYY-MM-DD HH:NN:SS', now));
Stomp := GetNewStompClient(GetClientID);
H := StompUtils.NewHeaders.Add(TStompHeaders.NewPersistentHeader(true));
Stomp.Send(ATopic, msg.ToString, H);
TThread.Sleep(100);
// single user cannot enqueue more than 10 message in noe second...
2013-10-30 00:48:23 +01:00
// it is noot too much elegant, but it works as DoS protection
finally
msg.Free;
end;
end;
2014-06-27 15:30:39 +02:00
function TMVCController.GetContentCharset: string;
2013-11-05 14:57:50 +01:00
begin
2014-06-27 15:30:39 +02:00
Result := FContentCharset;
2013-11-05 14:57:50 +01:00
end;
2013-10-30 00:48:23 +01:00
function TMVCController.GetContentType: string;
begin
Result := FContext.Response.ContentType;
end;
2013-11-10 01:04:17 +01:00
function TMVCController.GetCurrentWebModule: TWebModule;
begin
Result := GetMVCEngine.Owner as TWebModule;
end;
2013-10-30 00:48:23 +01:00
function TMVCController.GetNewStompClient(ClientID: string): IStompClient;
begin
2014-03-10 17:39:29 +01:00
Result := StompUtils.NewStomp(Config[TMVCConfigKey.StompServer],
2014-09-05 12:47:40 +02:00
StrToInt(Config[TMVCConfigKey.StompServerPort]), GetClientID,
Config[TMVCConfigKey.StompUsername], Config[TMVCConfigKey.StompPassword]);
2013-10-30 00:48:23 +01:00
end;
function TMVCController.GetWebSession: TWebSession;
begin
if not Assigned(FWebSession) then
begin
2014-09-05 12:47:40 +02:00
FWebSession := TMVCEngine.GetCurrentSession(GetMVCConfig,
Context.Request.FWebRequest, Context.Response.FWebResponse, '', false);
2013-10-30 00:48:23 +01:00
if not Assigned(FWebSession) then
SessionStart
// else
// SendSessionCookie(FWebSession.SessionID); //daniele
end;
Result := FWebSession;
Result.MarkAsUsed;
end;
procedure TMVCController.LoadView(const ViewName: string);
var
View: TMVCEmbeddedLuaView;
begin
try
2014-09-05 12:47:40 +02:00
View := TMVCEmbeddedLuaView.Create(ViewName, GetMVCEngine, FContext,
FViewModel, FViewDataSets, ContentType);
2013-10-30 00:48:23 +01:00
try
View.SetMVCConfig(GetMVCConfig);
View.Execute;
Render(View.Output);
finally
View.Free;
end;
except
on E: Exception do
begin
ContentType := 'text/plain';
Render(E);
end;
end;
end;
procedure TMVCController.MVCControllerAfterCreate;
begin
inherited;
end;
procedure TMVCController.MVCControllerBeforeDestroy;
begin
inherited;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.OnAfterAction(Context: TWebContext;
const AActionNAme: string);
2013-10-30 00:48:23 +01:00
begin
// do nothing
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.OnBeforeAction(Context: TWebContext;
const AActionNAme: string; var Handled: boolean);
2013-10-30 00:48:23 +01:00
begin
2013-11-10 01:04:17 +01:00
Handled := false;
2013-10-30 00:48:23 +01:00
if ContentType.IsEmpty then
2014-03-10 17:39:29 +01:00
ContentType := GetMVCConfig[TMVCConfigKey.DefaultContentType];
2013-10-30 00:48:23 +01:00
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.PushDataSetToView(const AModelName: string;
ADataSet: TDataSet);
2013-10-30 00:48:23 +01:00
begin
2014-04-10 13:56:23 +02:00
if not Assigned(FViewDataSets) then
FViewDataSets := TObjectDictionary<string, TDataSet>.Create;
2013-10-30 00:48:23 +01:00
FViewDataSets.Add(AModelName, ADataSet);
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.PushJSONToView(const AModelName: string;
AModel: TJSONValue);
2013-10-30 00:48:23 +01:00
begin
2014-04-10 13:56:23 +02:00
if not Assigned(FViewModel) then
FViewModel := TMVCDataObjects.Create;
2013-10-30 00:48:23 +01:00
FViewModel.Add(AModelName, AModel);
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.PushModelToView(const AModelName: string;
AModel: TObject);
2013-10-30 00:48:23 +01:00
begin
2014-04-10 13:56:23 +02:00
if not Assigned(FViewModel) then
FViewModel := TMVCDataObjects.Create;
2013-10-30 00:48:23 +01:00
FViewModel.Add(AModelName, AModel);
end;
2014-09-05 12:47:40 +02:00
procedure InternalRenderText(const AContent: string;
ContentType, ContentEncoding: string; Context: TWebContext);
2014-04-01 00:02:31 +02:00
var
OutEncoding: TEncoding;
begin
OutEncoding := TEncoding.GetEncoding(ContentEncoding);
2014-04-10 13:56:23 +02:00
try
2014-07-04 17:52:17 +02:00
// Context.Response.RawWebResponse.ContentStream := TStringStream.Create(UTF8Encode(AContent));
if SameText('UTF-8', ContentEncoding) then
begin
Context.Response.RawWebResponse.Content := '';
2014-09-05 12:47:40 +02:00
Context.Response.RawWebResponse.ContentStream :=
TStringStream.Create(UTF8Encode(AContent));
2014-07-04 17:52:17 +02:00
end
else
begin
Context.Response.RawWebResponse.Content :=
2014-09-05 12:47:40 +02:00
OutEncoding.GetString(TEncoding.Convert(TEncoding.UTF8, OutEncoding,
TEncoding.Default.GetBytes(AContent)));
2014-07-04 17:52:17 +02:00
end;
2014-04-10 13:56:23 +02:00
finally
OutEncoding.Free;
end;
2014-09-05 12:47:40 +02:00
Context.Response.RawWebResponse.ContentType := ContentType + '; charset=' +
ContentEncoding;
2014-04-10 13:56:23 +02:00
// Context.Response.RawWebResponse.ContentType := TMVCMimeType.APPLICATION_JSON;
// Context.Response.RawWebResponse.ContentEncoding := ContentEncoding;
// OutEncoding := TEncoding.GetEncoding(ContentEncoding);
// InEncoding := TEncoding.Default; // GetEncoding(S);
// Context.Response.Content := OutEncoding.GetString
// (TEncoding.Convert(InEncoding, OutEncoding, InEncoding.GetBytes(AContent)));
// OutEncoding.Free;
2014-04-01 00:02:31 +02:00
end;
2014-09-05 12:47:40 +02:00
procedure InternalRender(AJSONValue: TJSONValue;
ContentType, ContentEncoding: string; Context: TWebContext;
2013-12-04 13:06:18 +01:00
AInstanceOwner: boolean);
var
OutEncoding: TEncoding;
begin
OutEncoding := TEncoding.GetEncoding(ContentEncoding);
2014-04-10 13:56:23 +02:00
try
Context.Response.RawWebResponse.Content :=
2014-04-16 22:52:25 +02:00
OutEncoding.GetString(TEncoding.Convert(TEncoding.Default, OutEncoding,
2014-04-10 13:56:23 +02:00
TEncoding.Default.GetBytes(AJSONValue.ToString)));
finally
OutEncoding.Free;
end;
2014-09-05 12:47:40 +02:00
Context.Response.RawWebResponse.ContentType := ContentType + '; charset=' +
ContentEncoding;
2014-04-10 13:56:23 +02:00
// Context.Response.RawWebResponse.StatusCode := 200;
{
Context.Response.RawWebResponse.ContentType := TMVCMimeType.APPLICATION_JSON;
//Context.Response.RawWebResponse.ContentEncoding := ContentEncoding;
S := AJSONValue.ToString;
OutEncoding := TEncoding.GetEncoding(ContentEncoding);
InEncoding := TEncoding.Default;
Context.Response.RawWebResponse.Content := OutEncoding.GetString
2013-12-04 13:06:18 +01:00
(TEncoding.Convert(InEncoding, OutEncoding, InEncoding.GetBytes(S)));
2014-04-10 13:56:23 +02:00
OutEncoding.Free;
Context.Response.RawWebResponse.Content := s;
}
2013-12-04 13:06:18 +01:00
if AInstanceOwner then
FreeAndNil(AJSONValue)
end;
2014-09-05 12:47:40 +02:00
procedure InternalRender(const Content: string;
ContentType, ContentEncoding: string; Context: TWebContext);
2013-10-30 00:48:23 +01:00
begin
if ContentType = TMVCMimeType.APPLICATION_JSON then
begin
2014-09-05 12:47:40 +02:00
InternalRender(TJSONString.Create(Content), ContentType, ContentEncoding,
Context, true);
2013-10-30 00:48:23 +01:00
end
else if ContentType = TMVCMimeType.TEXT_XML then
begin
raise EMVCException.Create('Format still not supported - ' + ContentType);
end
else
2013-11-08 23:10:25 +01:00
begin
if ContentType.IsEmpty then
2014-04-01 00:02:31 +02:00
InternalRenderText(Content, 'text/plain', ContentEncoding, Context)
else
2014-04-01 00:02:31 +02:00
InternalRenderText(Content, ContentType, ContentEncoding, Context);
2013-11-08 23:10:25 +01:00
end;
2013-10-30 00:48:23 +01:00
end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.Render(const Content: string);
2013-12-04 13:06:18 +01:00
begin
2014-06-27 15:30:39 +02:00
InternalRender(Content, ContentType, ContentCharset, Context);
2013-12-04 13:06:18 +01:00
end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.Render(AObject: TObject; AInstanceOwner: boolean);
2013-10-30 00:48:23 +01:00
var
2014-04-16 22:52:25 +02:00
JSON: TJSONObject;
2013-10-30 00:48:23 +01:00
begin
2014-04-16 22:52:25 +02:00
JSON := Mapper.ObjectToJSONObject(AObject);
Render(JSON, true);
2013-10-30 00:48:23 +01:00
if AInstanceOwner then
FreeAndNil(AObject);
end;
procedure TMVCController.SendFile(AFileName: string);
begin
TMVCStaticContents.SendFile(AFileName, ContentType, Context);
/// ///daniele
// S := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
// FContext.Response.SetContentStream(S, ContentType);
end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.SendSessionCookie(const SessionID: string);
2013-10-30 00:48:23 +01:00
var
Cookie: TCookie;
begin
Cookie := FContext.FResponse.Cookies.Add;
Cookie.Name := TMVCConstants.SESSION_TOKEN_NAME;
Cookie.Value := SessionID;
Cookie.Expires := now + OneHour * 24 * 365;
// OneMinute * strtoint(GetMVCConfig['sessiontimeout']);
Cookie.Path := '/';
end;
procedure TMVCController.SendStream(AStream: TStream);
begin
FContext.Response.FWebResponse.Content := '';
// FContext.Response.SetContentStream(AStream, ContentType);
FContext.Response.FWebResponse.ContentType := ContentType;
FContext.Response.FWebResponse.ContentStream := AStream;
FContext.Response.FWebResponse.FreeContentStream := true;
2013-10-30 00:48:23 +01:00
end;
procedure TMVCController.SessionStart;
var
2013-11-08 23:10:25 +01:00
Sess: TWebSession;
2013-10-30 00:48:23 +01:00
SessionID: string;
begin
if not Assigned(FWebSession) then
begin
2014-09-05 12:47:40 +02:00
SessionID := StringReplace
(StringReplace(StringReplace(GUIDToString(TGUID.NewGuid), '}', '', []),
'{', '', []), '-', '', [rfReplaceAll]);
2013-10-30 00:48:23 +01:00
SendSessionCookie(SessionID);
TMonitor.Enter(SessionList);
try
2014-09-05 12:47:40 +02:00
Sess := TMVCSessionFactory.GetInstance.CreateNewByType('memory',
SessionID, StrToInt64(Config[TMVCConfigKey.SessionTimeout]));
2013-10-30 00:48:23 +01:00
SessionList.Add(SessionID, Sess);
FWebSession := Sess;
Sess.MarkAsUsed;
finally
TMonitor.Exit(SessionList);
end;
IsSessionStarted := true;
2013-11-10 01:04:17 +01:00
SessionMustBeClose := false;
2013-10-30 00:48:23 +01:00
end;
end;
procedure TMVCController.SessionStop(ARaiseExceptionIfExpired: boolean);
var
Cookie: TCookie;
begin
// Set-Cookie: token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT
FContext.FResponse.Cookies.Clear; // daniele ... remove all previous cookies
Cookie := FContext.FResponse.Cookies.Add;
Cookie.Name := TMVCConstants.SESSION_TOKEN_NAME;
// rubbish... invalid the cookie value
2014-09-05 12:47:40 +02:00
Cookie.Value := GUIDToString(TGUID.NewGuid) + 'invalid' +
GUIDToString(TGUID.NewGuid);
2013-10-30 00:48:23 +01:00
Cookie.Expires := EncodeDate(1970, 1, 1);
Cookie.Path := '/';
TMonitor.Enter(SessionList);
try
if not Assigned(FWebSession) then
2014-09-05 12:47:40 +02:00
FWebSession := TMVCEngine.GetCurrentSession(GetMVCConfig,
Context.Request.FWebRequest, Context.Response.FWebResponse, '',
ARaiseExceptionIfExpired);
2013-10-30 00:48:23 +01:00
if Assigned(FWebSession) then
SessionList.Remove(Session.SessionID);
finally
TMonitor.Exit(SessionList);
end;
2013-11-10 01:04:17 +01:00
IsSessionStarted := false;
2013-10-30 00:48:23 +01:00
SessionMustBeClose := true;
end;
2014-06-27 15:30:39 +02:00
procedure TMVCController.SetContentCharset(const Value: string);
2013-11-05 14:57:50 +01:00
begin
2014-06-27 15:30:39 +02:00
FContentCharset := Value;
2013-11-05 14:57:50 +01:00
end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.SetContentType(const Value: string);
2013-10-30 00:48:23 +01:00
begin
FContext.Response.ContentType := Value;
end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.SetContext(const Value: TWebContext);
2013-10-30 00:48:23 +01:00
begin
if FContext = nil then
FContext := Value
else
raise EMVCException.Create('Context already set');
end;
// procedure TMVCController.SetViewCache(const Value: TViewCache);
// begin
// FViewCache := Value;
// end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.SetWebSession(const Value: TWebSession);
2013-10-30 00:48:23 +01:00
begin
if Assigned(FWebSession) then
2014-09-05 12:47:40 +02:00
raise EMVCException.Create('Web Session already set for controller ' +
ClassName);
2013-10-30 00:48:23 +01:00
FWebSession := Value;
IsSessionStarted := Assigned(FWebSession);
end;
{ TMVCPathAttribute }
2014-04-16 22:52:25 +02:00
constructor MVCPathAttribute.Create(const Value: string);
2013-10-30 00:48:23 +01:00
begin
inherited Create;
FPath := Value;
end;
function TMVCWebRequest.Param(Name: string): string;
begin
Result := FWebRequest.QueryFields.Values[name];
end;
function TMVCWebRequest.QueryStringParam(Name: string): string;
begin
Result := FWebRequest.QueryFields.Values[name];
end;
2013-11-08 23:10:25 +01:00
function TMVCWebRequest.QueryStringParamExists(Name: string): boolean;
begin
2014-05-21 17:16:15 +02:00
Result := not QueryStringParam(name).IsEmpty;
2013-11-08 23:10:25 +01:00
end;
2013-10-30 00:48:23 +01:00
function TMVCWebRequest.GetClientPreferHTML: boolean;
begin
Result := ClientPrefer(TMVCMimeType.TEXT_HTML);
end;
function TMVCWebRequest.GetFiles: TAbstractWebRequestFiles;
begin
Result := FWebRequest.Files;
end;
2014-04-16 22:52:25 +02:00
function TMVCWebRequest.GetHeader(const Name: string): string;
2014-04-10 13:56:23 +02:00
begin
if Assigned(FWebRequest) then
Result := FWebRequest.GetFieldByName(name)
else
Result := '';
end;
2014-04-16 22:52:25 +02:00
function TMVCWebRequest.GetHeaderValue(const Name: string): string;
2014-04-10 13:56:23 +02:00
var
S: string;
2013-10-30 00:48:23 +01:00
begin
2014-05-21 17:16:15 +02:00
S := GetHeader(name);
2014-04-10 13:56:23 +02:00
if S.IsEmpty then
Result := ''
else
Result := S.Split([':'])[1].trim;
2013-10-30 00:48:23 +01:00
end;
// function TMVCWebRequest.GetHeaderAll(const HeaderName: string): string;
// begin
// Result := Self.FWebRequest.GetFieldByName(HeaderName);
// end;
function TMVCWebRequest.GetHTTPMethod: TMVCHTTPMethodType;
begin
Result := TMVCRouter.StringMethodToHTTPMetod(FWebRequest.Method);
end;
function TMVCWebRequest.GetHTTPMethodAsString: string;
begin
Result := FWebRequest.Method;
end;
function TMVCWebRequest.GetIsAjax: boolean;
begin
2014-09-05 12:47:40 +02:00
Result := LowerCase(FWebRequest.GetFieldByName('X-Requested-With'))
= 'xmlhttprequest';
2013-10-30 00:48:23 +01:00
end;
2014-04-16 22:52:25 +02:00
function TMVCWebRequest.GetParamAll(const ParamName: string): string;
2013-10-30 00:48:23 +01:00
begin
2014-09-05 12:47:40 +02:00
if (not Assigned(FParamsTable)) or
(not FParamsTable.TryGetValue(ParamName, Result)) then
2013-10-30 00:48:23 +01:00
begin
Result := FWebRequest.QueryFields.Values[ParamName];
if Result = EmptyStr then
Result := FWebRequest.ContentFields.Values[ParamName];
if Result = EmptyStr then
Result := FWebRequest.CookieFields.Values[ParamName];
end;
end;
2014-04-16 22:52:25 +02:00
function TMVCWebRequest.GetParamAllAsInteger(const ParamName: string): Integer;
2013-10-30 00:48:23 +01:00
begin
2013-12-04 13:06:18 +01:00
Result := StrToInt(GetParamAll(ParamName));
2013-10-30 00:48:23 +01:00
end;
2014-05-21 17:16:15 +02:00
function TMVCWebRequest.GetParamNames: TArray<string>;
2013-11-12 01:23:50 +01:00
var
I: Integer;
2014-05-21 17:16:15 +02:00
Names: TList<string>;
2013-11-12 01:23:50 +01:00
n: string;
begin
if Length(FParamNames) > 0 then
Exit(FParamNames);
2014-05-21 17:16:15 +02:00
Names := TList<string>.Create;
2013-11-12 01:23:50 +01:00
try
if Assigned(FParamsTable) and (Length(FParamsTable.Keys.ToArray) > 0) then
for n in FParamsTable.Keys.ToArray do
Names.Add(n);
if FWebRequest.QueryFields.Count > 0 then
for I := 0 to FWebRequest.QueryFields.Count - 1 do
Names.Add(FWebRequest.QueryFields.Names[I]);
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;
end;
2013-10-30 00:48:23 +01:00
function TMVCWebRequest.GetPathInfo: string;
begin
Result := FWebRequest.PathInfo;
end;
procedure TMVCWebRequest.SetParamsTable(AParamsTable: TMVCRequestParamsTable);
begin
FParamsTable := AParamsTable;
end;
function TMVCWebRequest.ThereIsRequestBody: boolean;
begin
Result := FWebRequest.Content <> '';
end;
{ MVCHTTPMethodAttribute }
constructor MVCHTTPMethodAttribute.Create(AMVCHTTPMethods: TMVCHTTPMethods);
begin
inherited Create;
FMVCHTTPMethods := AMVCHTTPMethods;
end;
function MVCHTTPMethodAttribute.GetMVCHTTPMethodsAsString: string;
var
I: TMVCHTTPMethodType;
begin
Result := '';
for I := low(TMVCHTTPMethodType) to high(TMVCHTTPMethodType) do
begin
if I in FMVCHTTPMethods then
begin
2014-09-05 12:47:40 +02:00
Result := Result + ',' + GetEnumName
(TypeInfo(TMVCHTTPMethodType), Ord(I));
2013-10-30 00:48:23 +01:00
end;
end;
if not Result.IsEmpty then
Result := Result.Remove(0, 1)
else
Result := 'any';
end;
{ TMVCStaticContents }
2014-09-05 12:47:40 +02:00
class procedure TMVCStaticContents.SendFile(AFileName, AMimeType: string;
Context: TWebContext);
2013-10-30 00:48:23 +01:00
var
LFileDate: TDateTime;
2013-11-08 23:10:25 +01:00
LReqDate: TDateTime;
S: TFileStream;
2013-10-30 00:48:23 +01:00
begin
LFileDate := IndyFileAge(AFileName);
if (LFileDate = 0.0) and (not FileExists(AFileName)) then
begin
Context.Response.StatusCode := 404;
end
else
begin
2014-09-05 12:47:40 +02:00
LReqDate := GMTToLocalDateTime(Context.Request.Headers
['If-Modified-Since']);
if (LReqDate <> 0) and (abs(LReqDate - LFileDate) < 2 * (1 / (24 * 60 * 60)))
then
2013-10-30 00:48:23 +01:00
begin
Context.Response.ContentType := AMimeType;
Context.Response.StatusCode := 304;
end
else
begin
S := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
2013-12-04 11:40:16 +01:00
Context.Response.SetCustomHeader('Content-Length', IntToStr(S.Size));
2014-09-05 12:47:40 +02:00
Context.Response.SetCustomHeader('Last-Modified',
LocalDateTimeToHttpStr(LFileDate));
2013-10-30 00:48:23 +01:00
Context.Response.SetContentStream(S, AMimeType);
end;
end;
end;
2014-09-05 12:47:40 +02:00
class function TMVCStaticContents.IsScriptableFile(StaticFileName: string;
Config: TMVCConfig): boolean;
2013-10-30 00:48:23 +01:00
begin
Result := TPath.GetExtension(StaticFileName).ToLower = '.' +
2014-03-10 17:39:29 +01:00
Config[TMVCConfigKey.DefaultViewFileExtension].ToLower;
2013-10-30 00:48:23 +01:00
end;
2014-09-05 12:47:40 +02:00
class function TMVCStaticContents.IsStaticFile(AViewPath, AWebRequestPath
: string; out ARealFileName: string): boolean;
2013-10-30 00:48:23 +01:00
var
FileName: string;
begin
if TDirectory.Exists(AViewPath) then // absolute path
2014-09-05 12:47:40 +02:00
FileName := AViewPath + AWebRequestPath.Replace('/',
TPath.DirectorySeparatorChar)
2013-10-30 00:48:23 +01:00
else
FileName := GetApplicationFileNamePath + AViewPath +
// relative path
AWebRequestPath.Replace('/', TPath.DirectorySeparatorChar);
Result := TFile.Exists(FileName);
ARealFileName := FileName;
end;
2014-04-16 22:52:25 +02:00
procedure TMVCBase.SetApplicationSession(const Value: TWebApplicationSession);
2013-10-30 00:48:23 +01:00
begin
if Assigned(FApplicationSession) then
raise EMVCException.Create('Application Session already set');
FApplicationSession := Value;
end;
2014-04-16 22:52:25 +02:00
procedure TMVCBase.SetMVCConfig(const Value: TMVCConfig);
2013-10-30 00:48:23 +01:00
begin
FMVCConfig := Value;
end;
2014-04-16 22:52:25 +02:00
procedure TMVCBase.SetMVCEngine(const Value: TMVCEngine);
2013-10-30 00:48:23 +01:00
begin
FMVCEngine := Value;
end;
2014-09-05 12:47:40 +02:00
class function TMVCBase.GetApplicationFileName: string;
2013-10-30 00:48:23 +01:00
var
fname: PChar;
2013-11-08 23:10:25 +01:00
Size: Integer;
2013-10-30 00:48:23 +01:00
begin
Result := '';
fname := GetMemory(2048);
try
Size := GetModuleFileName(0, fname, 2048);
if Size > 0 then
Result := fname;
finally
FreeMem(fname, 2048);
end;
end;
2014-09-05 12:47:40 +02:00
class function TMVCBase.GetApplicationFileNamePath: string;
2013-10-30 00:48:23 +01:00
begin
2014-09-05 12:47:40 +02:00
Result := IncludeTrailingPathDelimiter
(ExtractFilePath(GetApplicationFileName));
2013-10-30 00:48:23 +01:00
end;
function TMVCBase.GetMVCConfig: TMVCConfig;
begin
if not Assigned(FMVCConfig) then
2014-09-05 12:47:40 +02:00
EMVCConfigException.Create('MVCConfig not assigned to this ' + ClassName +
' instances');
2013-10-30 00:48:23 +01:00
Result := FMVCConfig;
end;
function TMVCBase.GetMVCEngine: TMVCEngine;
begin
Result := FMVCEngine;
end;
{ TMVCISAPIWebRequest }
function TMVCISAPIWebRequest.ClientIP: string;
begin
raise EMVCException.Create('<TMVCISAPIWebRequest.ClientIP> Not implemented');
2013-10-30 00:48:23 +01:00
end;
constructor TMVCISAPIWebRequest.Create(AWebRequest: TWebRequest);
begin
inherited;
FWebRequest := AWebRequest as TISAPIRequest;
end;
{ TMVCApacheWebRequest }
{$IF CompilerVersion >= 27}
function TMVCApacheWebRequest.ClientIP: string;
begin
raise EMVCException.Create('<TMVCApacheWebRequest.ClientIP> Not implemented');
end;
constructor TMVCApacheWebRequest.Create(AWebRequest: TWebRequest);
begin
inherited;
FWebRequest := AWebRequest as TApacheRequest;
end;
{$ENDIF}
2013-10-30 00:48:23 +01:00
{ TMVCINDYWebRequest }
function TMVCINDYWebRequest.ClientIP: string;
{
This code has been converted to Delphi from a PHP code
http://www.grantburton.com/2008/11/30/fix-for-incorrect-ip-addresses-in-wordpress-comments/
}
function CheckIP(IP: string): boolean;
begin
Result := (not IP.IsEmpty) and { (IP2Long(IP) <> -1) and }
(IP2Long(IP) > 0);
2013-10-30 00:48:23 +01:00
end;
var
2013-11-08 23:10:25 +01:00
S: string;
2013-10-30 00:48:23 +01:00
req: TIdHTTPAppRequestHack;
2013-11-05 14:57:50 +01:00
2013-11-08 23:10:25 +01:00
{$IFDEF IOCP}
2013-10-30 00:48:23 +01:00
Headers: TStringList;
2013-11-05 14:57:50 +01:00
2013-11-08 23:10:25 +01:00
{$ELSE}
2013-10-30 00:48:23 +01:00
Headers: TIdHeaderList;
2013-11-05 14:57:50 +01:00
2013-11-08 23:10:25 +01:00
{$ENDIF}
2013-10-30 00:48:23 +01:00
begin
req := TIdHTTPAppRequestHack(FWebRequest);
2013-11-05 14:57:50 +01:00
2013-11-08 23:10:25 +01:00
{$IFDEF IOCP}
2013-10-30 00:48:23 +01:00
Headers := req.FHttpConnection.RequestHeader;
2013-11-05 14:57:50 +01:00
2013-11-08 23:10:25 +01:00
{$ELSE}
2013-10-30 00:48:23 +01:00
Headers := req.FRequestInfo.RawHeaders;
2013-11-05 14:57:50 +01:00
2013-11-08 23:10:25 +01:00
{$ENDIF}
2013-10-30 00:48:23 +01:00
if CheckIP(Headers.Values['HTTP_CLIENT_IP']) then
Exit(Headers.Values['HTTP_CLIENT_IP']);
for S in Headers.Values['HTTP_X_FORWARDED_FOR'].Split([',']) do
begin
if CheckIP(S.trim) then
Exit(S.trim);
2013-10-30 00:48:23 +01:00
end;
if CheckIP(Headers.Values['HTTP_X_FORWARDED']) then
Exit(Headers.Values['HTTP_X_FORWARDED']);
if CheckIP(Headers.Values['HTTP_X_CLUSTER_CLIENT_IP']) then
Exit(Headers.Values['HTTP_X_CLUSTER_CLIENT_IP']);
if CheckIP(Headers.Values['HTTP_FORWARDED_FOR']) then
Exit(Headers.Values['HTTP_FORWARDED_FOR']);
if CheckIP(Headers.Values['HTTP_FORWARDED']) then
Exit(Headers.Values['HTTP_FORWARDED']);
if CheckIP(Headers.Values['REMOTE_ADDR']) then
Exit(Headers.Values['REMOTE_ADDR']);
if CheckIP(FWebRequest.RemoteIP) then
Exit(FWebRequest.RemoteIP);
if CheckIP(FWebRequest.RemoteAddr) then
Exit(FWebRequest.RemoteAddr);
if CheckIP(FWebRequest.RemoteHost) then
Exit(FWebRequest.RemoteHost);
Result := '';
end;
constructor TMVCINDYWebRequest.Create(AWebRequest: TWebRequest);
begin
inherited;
FWebRequest := AWebRequest; // as TIdHTTPAppRequest;
end;
{ TWebSession }
procedure TMVCController.RaiseSessionExpired;
begin
raise EMVCSessionExpiredException.Create('Session expired');
end;
2014-09-05 12:47:40 +02:00
function TMVCController.ReceiveMessageFromTopic(const ATopic: string;
ATimeout: Int64; var JSONObject: TJSONObject): boolean;
2013-10-30 00:48:23 +01:00
var
Stomp: IStompClient;
frame: IStompFrame;
2013-11-08 23:10:25 +01:00
o: TJSONValue;
2013-10-30 00:48:23 +01:00
begin
2013-11-10 01:04:17 +01:00
Result := false;
2013-10-30 00:48:23 +01:00
Stomp := GetNewStompClient(GetClientID);
if not Stomp.Receive(frame, ATimeout) then
JSONObject := nil
else
begin
o := TJSONObject.ParseJSONValue(frame.GetBody);
if not Assigned(o) then
raise EMVCException.Create('Message is not a valid JSONObject')
else
begin
2013-11-08 23:10:25 +01:00
if not(o is TJSONObject) then
2013-10-30 00:48:23 +01:00
begin
FreeAndNil(o);
2014-09-05 12:47:40 +02:00
raise EMVCException.Create
('Message is a JSONValue but not a JSONObject')
2013-10-30 00:48:23 +01:00
end
else
JSONObject := TJSONObject(o);
end;
end;
end;
procedure TMVCController.Redirect(const URL: string);
begin
FContext.Response.FWebResponse.SendRedirect(URL);
end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.Render(E: Exception; ErrorItems: TList<string>);
2013-10-30 00:48:23 +01:00
var
2013-11-08 23:10:25 +01:00
j: TJSONObject;
S: string;
2013-10-30 00:48:23 +01:00
jarr: TJSONArray;
begin
if E is EMVCException then
ResponseStatusCode(EMVCException(E).HTTPErrorCode)
else
begin
if Context.Response.StatusCode = 200 then
ResponseStatusCode(500);
end;
2013-11-11 12:23:49 +01:00
if (not Context.Request.IsAjax) and (Context.Request.ClientPreferHTML) then
begin
ContentType := TMVCMimeType.TEXT_HTML;
2014-06-27 15:30:39 +02:00
ContentCharset := TMVCConstants.DEFAULT_CONTENT_CHARSET;
2013-11-11 12:23:49 +01:00
ResponseStream.Clear;
2014-04-16 22:52:25 +02:00
ResponseStream.Append
('<html><head><style>pre { color: #000000; background-color: #d0d0d0; }</style></head><body>')
2014-09-05 12:47:40 +02:00
.Append('<h1>DMVCFramework: Error Raised</h1>')
.AppendFormat('<pre>HTTP Return Code: %d' + sLineBreak,
[Context.Response.StatusCode])
.AppendFormat('HTTP Reason Text: "%s"</pre>',
[Context.Response.ReasonString]).Append('<h3><pre>')
.AppendFormat('Exception Class Name : %s' + sLineBreak, [E.ClassName])
.AppendFormat('Exception Message : %s' + sLineBreak, [E.Message])
.Append('</pre></h3>');
2013-11-11 12:23:49 +01:00
if Assigned(ErrorItems) and (ErrorItems.Count > 0) then
begin
ResponseStream.Append('<h2><pre>');
for S in ErrorItems do
ResponseStream.AppendLine('- ' + S);
ResponseStream.Append('</pre><h2>');
end
else
begin
ResponseStream.AppendLine('<pre>No other informations available</pre>');
end;
ResponseStream.Append('</body></html>');
Render;
end
else if Context.Request.IsAjax or (ContentType = 'application/json') then
2013-10-30 00:48:23 +01:00
begin
j := TJSONObject.Create;
j.AddPair('status', 'error');
j.AddPair('classname', E.ClassName);
j.AddPair('message', E.Message);
2013-10-30 00:48:23 +01:00
j.AddPair('http_error', TJSONNumber.Create(Context.Response.StatusCode));
if Assigned(ErrorItems) then
begin
jarr := TJSONArray.Create;
j.AddPair('erroritems', jarr);
for S in ErrorItems do
begin
jarr.AddElement(TJSONString.Create(S));
end;
end;
Render(j);
end
else
begin
Render(Format('Exception: [%s] %s', [E.ClassName, E.Message]));
end;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.Render(const AErrorCode: UInt16;
2014-10-03 11:40:57 +02:00
const AErrorMessage: string; const AErrorClassName: string = '');
2013-10-30 00:48:23 +01:00
var
2013-11-08 23:10:25 +01:00
j: TJSONObject;
2013-10-30 00:48:23 +01:00
status: string;
begin
ResponseStatusCode(AErrorCode);
if Context.Request.IsAjax or (ContentType = 'application/json') then
begin
status := 'error';
if (AErrorCode div 100) = 2 then
status := 'ok';
j := TJSONObject.Create;
j.AddPair('status', status);
2014-10-03 11:40:57 +02:00
if AErrorClassName = '' then
j.AddPair('classname', TJSONNull.Create)
else
j.AddPair('classname', AErrorClassName);
2013-10-30 00:48:23 +01:00
j.AddPair('message', AErrorMessage);
Render(j);
end
else
begin
Render(Format('Error: [%d] %s', [AErrorCode, AErrorMessage]));
end;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.Render(ADataSet: TDataSet; AInstanceOwner: boolean;
2014-10-26 20:48:52 +01:00
AOnlySingleRecord: boolean; AJSONObjectActionProc: TJSONObjectActionProc);
2013-11-10 01:04:17 +01:00
var
arr: TJSONArray;
JObj: TJSONObject;
2013-11-10 01:04:17 +01:00
begin
if ContentType = TMVCMimeType.APPLICATION_JSON then
begin
2014-03-07 23:16:33 +01:00
if not AOnlySingleRecord then
begin
ADataSet.First;
arr := TJSONArray.Create;
2014-10-26 20:48:52 +01:00
Mapper.DataSetToJSONArray(ADataSet, arr, AInstanceOwner,
AJSONObjectActionProc);
2014-03-07 23:16:33 +01:00
Render(arr);
end
else
begin
JObj := TJSONObject.Create;
2014-10-26 20:48:52 +01:00
Mapper.DataSetToJSONObject(ADataSet, JObj, AInstanceOwner,
AJSONObjectActionProc);
Render(JObj);
2014-03-07 23:16:33 +01:00
end;
2013-11-10 01:04:17 +01:00
end
else
2014-09-05 12:47:40 +02:00
raise Exception.Create('ContentType not supported for this render [' +
ContentType + ']');
2013-11-10 01:04:17 +01:00
// if ContentType = TMVCMimeType.TEXT_XML then
// begin
// Mapper.DataSetToXML(ADataSet, S, AInstanceOwner);
// Render(S);
// end;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.Render<T>(ACollection: TObjectList<T>;
AInstanceOwner: boolean; AJSONObjectActionProc: TJSONObjectActionProc);
2013-10-30 00:48:23 +01:00
var
2014-04-16 22:52:25 +02:00
JSON: TJSONArray;
2013-10-30 00:48:23 +01:00
begin
2014-09-05 12:47:40 +02:00
JSON := Mapper.ObjectListToJSONArray<T>(ACollection, false,
AJSONObjectActionProc);
2014-04-16 22:52:25 +02:00
Render(JSON, true);
2013-10-30 00:48:23 +01:00
if AInstanceOwner then
FreeAndNil(ACollection);
end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.RenderListAsProperty<T>(const APropertyName: string;
2014-09-05 12:47:40 +02:00
AObjectList: TObjectList<T>; AOwnsInstance: boolean;
AJSONObjectActionProc: TJSONObjectActionProc);
2013-10-30 00:48:23 +01:00
begin
2014-09-05 12:47:40 +02:00
Render(TJSONObject.Create(TJSONPair.Create(APropertyName,
Mapper.ObjectListToJSONArray<T>(AObjectList, AOwnsInstance,
2014-04-16 22:52:25 +02:00
AJSONObjectActionProc))));
2013-10-30 00:48:23 +01:00
end;
2014-04-15 17:03:47 +02:00
procedure TMVCController.RenderStreamAndFree(const AStream: TStream);
begin
SendStream(AStream);
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.Render(AJSONValue: TJSONValue;
AInstanceOwner: boolean);
2013-12-04 13:06:18 +01:00
begin
2014-09-05 12:47:40 +02:00
InternalRender(AJSONValue, ContentType, ContentCharset, Context,
AInstanceOwner);
2013-10-30 00:48:23 +01:00
end;
2014-04-16 22:52:25 +02:00
procedure TMVCController.ResponseStatusCode(const ErrorCode: UInt16);
2013-10-30 00:48:23 +01:00
begin
Context.Response.StatusCode := ErrorCode;
end;
function TMVCController.ResponseStream: TStringBuilder;
begin
if not Assigned(FResponseStream) then
FResponseStream := TStringBuilder.Create;
Result := FResponseStream;
end;
constructor MVCPathAttribute.Create;
begin
Create('');
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.Render(const AErrorCode: UInt16;
AJSONValue: TJSONValue; AInstanceOwner: boolean);
2013-10-30 00:48:23 +01:00
begin
ResponseStatusCode(AErrorCode);
if ContentType = 'application/json' then
begin
Render(AJSONValue, AInstanceOwner);
end
else
begin
2014-09-05 12:47:40 +02:00
raise EMVCException.Create
('Cannot render a JSONValue if ContentType is not application/json');
2013-10-30 00:48:23 +01:00
end;
end;
2014-09-05 12:47:40 +02:00
procedure TMVCController.Render(const AErrorCode: UInt16; AObject: TObject;
AInstanceOwner: boolean);
2013-10-30 00:48:23 +01:00
begin
Render(AErrorCode, Mapper.ObjectToJSONObject(AObject), true);
if AInstanceOwner then
AObject.Free;
end;
procedure TMVCController.Render;
begin
Render(ResponseStream.ToString);
end;
{$IFDEF IOCP}
constructor TMVCIOCPWebRequest.Create(AWebRequest: TWebRequest);
begin
inherited;
FWebRequest := AWebRequest as TIocpWebRequest;
end;
{$ENDIF}
{ MVCStringAttribute }
2014-04-16 22:52:25 +02:00
constructor MVCStringAttribute.Create(const Value: string);
2013-10-30 00:48:23 +01:00
begin
inherited Create;
FValue := Value;
end;
function IsShuttingDown: boolean;
begin
Result := TInterlocked.Read(_IsShuttingDown) = 1
end;
procedure EnterInShutdownState;
begin
TInterlocked.Add(_IsShuttingDown, 1);
end;
2013-11-08 23:10:25 +01:00
{ MVCProduceAttribute }
2014-05-21 17:16:15 +02:00
constructor MVCProducesAttribute.Create(const Value, ProduceEncoding: string);
2013-11-08 23:10:25 +01:00
begin
Create(Value);
FProduceEncoding := ProduceEncoding;
end;
2014-04-16 22:52:25 +02:00
constructor MVCProducesAttribute.Create(const Value: string);
2013-11-08 23:10:25 +01:00
begin
inherited;
FProduceEncoding := 'UTF-8';
end;
2014-05-21 17:16:15 +02:00
procedure MVCProducesAttribute.SetProduceEncoding(const Value: string);
2013-11-08 23:10:25 +01:00
begin
FProduceEncoding := Value;
end;
2013-10-30 00:48:23 +01:00
initialization
_IsShuttingDown := 0;
end.