2016-06-22 17:49:16 +02:00
|
|
|
|
// ***************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// Delphi MVC Framework
|
|
|
|
|
//
|
|
|
|
|
// Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team
|
|
|
|
|
//
|
|
|
|
|
// https://github.com/danieleteti/delphimvcframework
|
|
|
|
|
//
|
|
|
|
|
// ***************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
{$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.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
|
2016-02-28 19:06:05 +01:00
|
|
|
|
{$IF CompilerVersion < 27}
|
2015-12-02 04:14:15 +01:00
|
|
|
|
, Data.DBXJSON
|
2014-09-05 12:47:40 +02:00
|
|
|
|
{$ELSE}
|
2016-09-25 22:31:33 +02:00
|
|
|
|
, System.JSON
|
2016-02-28 19:06:05 +01:00
|
|
|
|
{$ENDIF}
|
2016-09-27 13:49:24 +02:00
|
|
|
|
{$IF CompilerVersion >= 27}
|
2016-10-02 17:44:20 +02:00
|
|
|
|
, Web.ApacheHTTP
|
|
|
|
|
// Apache Support since XE6 http://docwiki.embarcadero.com/Libraries/XE6/de/Web.ApacheHTTP
|
2016-09-27 13:49:24 +02:00
|
|
|
|
{$ENDIF}
|
2016-09-06 10:30:52 +02:00
|
|
|
|
, ReqMulti {Delphi XE4 (all update) and XE5 (with no update) dont contains this unit. Look for the bug in QC}
|
2016-09-30 11:44:11 +02:00
|
|
|
|
, LoggerPro, MVCFramework.DuckTyping;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
|
|
|
|
type
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMVCHTTPMethodType = (httpGET, httpPOST, httpPUT, httpDELETE, httpHEAD,
|
|
|
|
|
httpOPTIONS, httpPATCH, httpTRACE);
|
|
|
|
|
TMVCHTTPMethods = set of TMVCHTTPMethodType;
|
|
|
|
|
|
|
|
|
|
TDMVCSerializationType = TSerializationType;
|
2016-08-09 13:08:17 +02:00
|
|
|
|
TSessionData = TDictionary<string, string>;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
2015-12-16 16:41:06 +01:00
|
|
|
|
MVCHTTPMethodsAttribute = MVCHTTPMethodAttribute; // just an alias
|
2015-12-16 15:57:20 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
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;
|
|
|
|
|
|
2015-12-16 15:57:20 +01:00
|
|
|
|
MVCDocAttribute = class(MVCStringAttribute)
|
|
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
MVCProducesAttribute = class(MVCStringAttribute)
|
|
|
|
|
private
|
|
|
|
|
FProduceEncoding: string;
|
|
|
|
|
procedure SetProduceEncoding(const Value: string);
|
|
|
|
|
public
|
|
|
|
|
constructor Create(const Value: string); overload;
|
|
|
|
|
constructor Create(const Value: string;
|
|
|
|
|
const ProduceEncoding: string); overload;
|
|
|
|
|
property ProduceEncoding: string read FProduceEncoding
|
|
|
|
|
write SetProduceEncoding;
|
|
|
|
|
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;
|
|
|
|
|
private
|
2016-09-25 16:17:37 +02:00
|
|
|
|
FBody: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FWebRequest: TWebRequest;
|
|
|
|
|
FParamsTable: TMVCRequestParamsTable;
|
|
|
|
|
FContentType: string;
|
|
|
|
|
FCharset: string;
|
|
|
|
|
FContentCharset: string;
|
|
|
|
|
function GetHeader(const Name: string): string;
|
|
|
|
|
// function GetHeaderValue(const Name: string): string;
|
|
|
|
|
function GetPathInfo: string;
|
|
|
|
|
function GetParamAll(const ParamName: string): string;
|
2016-08-09 13:08:17 +02:00
|
|
|
|
function GetSegmentParam(const ParamName: string; out Value: string): Boolean;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function GetSegmentParamsCount: Integer;
|
|
|
|
|
function GetIsAjax: Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
function GetHTTPMethod: TMVCHTTPMethodType;
|
|
|
|
|
function GetHTTPMethodAsString: string;
|
|
|
|
|
function GetParamAllAsInteger(const ParamName: string): Integer;
|
2016-09-27 14:33:51 +02:00
|
|
|
|
function GetParamAllAsInt64(const ParamName: string): Int64;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function GetClientPreferHTML: Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
function GetFiles: TAbstractWebRequestFiles;
|
|
|
|
|
|
|
|
|
|
strict protected
|
|
|
|
|
FBodyAsJSONValue: TJSONValue;
|
|
|
|
|
FParamNames: TArray<string>;
|
|
|
|
|
public
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
procedure SetParamsTable(AParamsTable: TMVCRequestParamsTable);
|
|
|
|
|
function GetParamNames: TArray<string>;
|
2015-12-16 15:57:20 +01:00
|
|
|
|
function ClientIP: string; virtual;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function ClientPrefer(MimeType: string): Boolean;
|
|
|
|
|
function ThereIsRequestBody: Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
function Accept: string;
|
|
|
|
|
function QueryStringParam(Name: string): string; virtual;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function QueryStringParamExists(Name: string): Boolean; virtual;
|
2016-06-23 12:11:01 +02:00
|
|
|
|
function QueryStringParams: TStrings;
|
2016-08-09 13:08:17 +02:00
|
|
|
|
procedure EnsureQueryParamExists(const Name: string);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
function ContentParam(Name: string): string; virtual;
|
|
|
|
|
function Cookie(Name: string): string; virtual;
|
|
|
|
|
property PathInfo: string read GetPathInfo;
|
|
|
|
|
function Body: string;
|
|
|
|
|
function BodyAs<T: class, constructor>(const RootProperty: string = ''): T;
|
|
|
|
|
function BodyAsListOf<T: class, constructor>(const RootProperty
|
|
|
|
|
: string = ''): TObjectList<T>;
|
|
|
|
|
function BodyAsJSONObject: TJSONObject;
|
|
|
|
|
function BodyAsJSONValue: TJSONValue;
|
|
|
|
|
property Headers[const HeaderName: string]: string read GetHeader;
|
|
|
|
|
property ParamsAsInteger[const ParamName: string]: Integer
|
|
|
|
|
read GetParamAllAsInteger;
|
2016-09-27 14:33:51 +02:00
|
|
|
|
property ParamsAsInt64[const ParamName: string]: Int64
|
|
|
|
|
read GetParamAllAsInt64;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
property Params[const ParamName: string]: string read GetParamAll;
|
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 RawWebRequest: TWebRequest read FWebRequest;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
property ClientPreferHTML: Boolean read GetClientPreferHTML;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
property Files: TAbstractWebRequestFiles read GetFiles;
|
|
|
|
|
property ContentType: string read FContentType;
|
|
|
|
|
property ContentCharset: string read FContentCharset;
|
|
|
|
|
property Charset: string read FCharset;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2014-09-29 17:42:34 +02:00
|
|
|
|
{$IF CompilerVersion >= 27}
|
2014-10-03 11:40:57 +02:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMVCApacheWebRequest = class(TMVCWebRequest)
|
|
|
|
|
public
|
|
|
|
|
constructor Create(AWebRequest: TWebRequest); override;
|
|
|
|
|
end;
|
2014-05-14 15:55:41 +02:00
|
|
|
|
{$ENDIF}
|
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMVCISAPIWebRequest = class(TMVCWebRequest)
|
|
|
|
|
public
|
|
|
|
|
constructor Create(AWebRequest: TWebRequest); override;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMVCINDYWebRequest = class(TMVCWebRequest)
|
|
|
|
|
public
|
|
|
|
|
constructor Create(AWebRequest: TWebRequest); override;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMVCWebResponse = class
|
|
|
|
|
strict private
|
|
|
|
|
function GetCustomHeaders: TStrings;
|
|
|
|
|
|
|
|
|
|
private
|
2016-06-22 17:49:16 +02:00
|
|
|
|
FStreamOutputDone: Boolean;
|
|
|
|
|
FFlushOnDestroy: Boolean; // tristan
|
2015-12-02 04:14:15 +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;
|
|
|
|
|
function GetLocation: string;
|
|
|
|
|
procedure SetLocation(const Value: string);
|
|
|
|
|
function GetReasonString: string;
|
|
|
|
|
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 GetReasonString write SetReasonString;
|
|
|
|
|
property Cookies: TCookieCollection read GetCookies;
|
|
|
|
|
property ContentType: string read GetContentType write SetContentType;
|
|
|
|
|
property Location: string read GetLocation write SetLocation;
|
|
|
|
|
property RawWebResponse: TWebResponse read FWebResponse;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
property FlushOnDestroy: Boolean read FFlushOnDestroy write FFlushOnDestroy;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// tristan
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TMVCEngine = class;
|
|
|
|
|
|
|
|
|
|
TUser = class
|
|
|
|
|
private
|
|
|
|
|
FRoles: TList<string>;
|
|
|
|
|
FUserName: string;
|
|
|
|
|
FLoggedSince: TDateTime;
|
|
|
|
|
FRealm: string;
|
|
|
|
|
procedure SetUserName(const Value: string);
|
|
|
|
|
procedure SetLoggedSince(const Value: TDateTime);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function GetIsValidLoggedUser: Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure SetRealm(const Value: string);
|
|
|
|
|
|
|
|
|
|
public
|
|
|
|
|
procedure SaveToSession(AWebSession: TWebSession);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function LoadFromSession(AWebSession: TWebSession): Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure Clear;
|
|
|
|
|
property Roles: TList<string> read FRoles;
|
|
|
|
|
property UserName: string read FUserName write SetUserName;
|
|
|
|
|
property LoggedSince: TDateTime read FLoggedSince write SetLoggedSince;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
property IsValid: Boolean read GetIsValidLoggedUser;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
property Realm: string read FRealm write SetRealm;
|
|
|
|
|
constructor Create; virtual;
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TWebContext = class
|
|
|
|
|
private
|
|
|
|
|
FRequest: TMVCWebRequest;
|
|
|
|
|
FResponse: TMVCWebResponse;
|
|
|
|
|
FConfig: TMVCConfig;
|
|
|
|
|
FParamsTable: TMVCRequestParamsTable;
|
|
|
|
|
FData: TDictionary<string, string>;
|
|
|
|
|
FLoggedUser: TUser;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
FWebSession: TWebSession;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
FIsSessionStarted: Boolean;
|
|
|
|
|
FSessionMustBeClose: Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
function GetData: TDictionary<string, string>;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
function GetWebSession: TWebSession;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
protected
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function SessionMustBeClose: Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
constructor Create(ARequest: TWebRequest; AResponse: TWebResponse;
|
|
|
|
|
AConfig: TMVCConfig); virtual;
|
|
|
|
|
procedure SetParams(AParamsTable: TMVCRequestParamsTable);
|
|
|
|
|
procedure Flush;
|
|
|
|
|
function GetLoggedUser: TUser;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
// Session
|
2016-06-28 13:42:14 +02:00
|
|
|
|
function IsSessionStarted: Boolean;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
procedure SessionStart; virtual;
|
|
|
|
|
procedure BindToSession(SessionID: string);
|
|
|
|
|
function SendSessionCookie(AContext: TWebContext): string;
|
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
public
|
|
|
|
|
destructor Destroy; override;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure SessionStop(ARaiseExceptionIfExpired: Boolean = true); virtual;
|
2016-06-28 13:42:14 +02:00
|
|
|
|
function SessionStarted: Boolean;
|
2016-08-09 13:08:17 +02:00
|
|
|
|
function SessionID: string;
|
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;
|
|
|
|
|
property Data: TDictionary<string, string> read GetData;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TMVCActionProc = reference to procedure(Context: TWebContext);
|
|
|
|
|
|
|
|
|
|
TMVCBase = class(TObject)
|
|
|
|
|
private
|
|
|
|
|
FMVCEngine: TMVCEngine;
|
|
|
|
|
FMVCConfig: TMVCConfig;
|
|
|
|
|
FApplicationSession: TWebApplicationSession;
|
|
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
class function GetApplicationFileName: string;
|
|
|
|
|
class function GetApplicationFileNamePath: string;
|
|
|
|
|
procedure SetApplicationSession(const Value: TWebApplicationSession);
|
|
|
|
|
|
|
|
|
|
public
|
|
|
|
|
procedure SetMVCConfig(const Value: TMVCConfig);
|
|
|
|
|
function GetMVCConfig: TMVCConfig;
|
|
|
|
|
procedure SetMVCEngine(const Value: TMVCEngine);
|
|
|
|
|
function GetMVCEngine: TMVCEngine;
|
|
|
|
|
property ApplicationSession: TWebApplicationSession read FApplicationSession
|
|
|
|
|
write SetApplicationSession;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TMVCController = class(TMVCBase)
|
|
|
|
|
private
|
|
|
|
|
FViewModel: TMVCDataObjects;
|
|
|
|
|
FViewDataSets: TObjectDictionary<string, TDataSet>;
|
|
|
|
|
FContext: TWebContext;
|
|
|
|
|
FResponseStream: TStringBuilder;
|
|
|
|
|
FContentCharset: string;
|
|
|
|
|
procedure SetContext(const Value: TWebContext);
|
|
|
|
|
procedure SetWebSession(const Value: TWebSession);
|
|
|
|
|
procedure SetContentType(const Value: string);
|
|
|
|
|
function GetContentType: string;
|
|
|
|
|
function GetWebSession: TWebSession;
|
|
|
|
|
function GetContentCharset: string;
|
|
|
|
|
procedure SetContentCharset(const Value: string);
|
|
|
|
|
// procedure Render<T: class>(ACollection: TObjectList<T>; AInstanceOwner: boolean;
|
|
|
|
|
// AJSONObjectActionProc: TJSONObjectActionProc; ASerializationType: TSerializationType); overload;
|
|
|
|
|
// procedure Render<T: class>(ACollection: TObjectList<T>; AInstanceOwner: boolean;
|
|
|
|
|
// AJSONObjectActionProc: TJSONObjectActionProc; ASerializationType: TSerializationType);
|
|
|
|
|
|
|
|
|
|
protected const
|
|
|
|
|
CLIENTID_KEY = '__clientid';
|
|
|
|
|
protected
|
|
|
|
|
function GetClientID: string;
|
|
|
|
|
procedure RaiseSessionExpired; virtual;
|
|
|
|
|
function GetCurrentWebModule: TWebModule;
|
|
|
|
|
function ResponseStream: TStringBuilder;
|
|
|
|
|
function GetNewStompClient(ClientID: string = ''): IStompClient;
|
2016-02-27 09:58:54 +01:00
|
|
|
|
/// <summary>
|
2016-09-25 16:17:37 +02:00
|
|
|
|
/// Load mustache view located in TMVCConfigKey.ViewsPath
|
|
|
|
|
/// returns the rendered views and generates output using
|
|
|
|
|
/// models pushed using Push* methods
|
2016-02-27 09:58:54 +01:00
|
|
|
|
/// </summary>
|
2016-09-25 16:17:37 +02:00
|
|
|
|
function LoadView(const ViewNames: TArray<string>): string; virtual;
|
2016-10-02 17:44:20 +02:00
|
|
|
|
|
|
|
|
|
/// <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(ViewFragment);</code>
|
|
|
|
|
/// </summary>
|
|
|
|
|
procedure LoadViewFragment(const ViewFragment: string);
|
|
|
|
|
|
2016-02-27 09:58:54 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Load mustache view located in TMVCConfigKey.ViewsPath and
|
|
|
|
|
/// returns output using models pushed using Push* methods
|
|
|
|
|
/// </summary>
|
2016-08-09 13:08:17 +02:00
|
|
|
|
function GetRenderedView(const ViewNames: TArray<string>): string; virtual;
|
2016-12-05 15:50:00 +01:00
|
|
|
|
function SessionAs<T: TWebSession>: T;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
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;
|
|
|
|
|
property ContentCharset: string read GetContentCharset
|
|
|
|
|
write SetContentCharset;
|
|
|
|
|
// Renderers
|
|
|
|
|
procedure Render(const Content: string); overload; virtual;
|
2016-09-25 16:17:37 +02:00
|
|
|
|
procedure Render; overload; virtual; deprecated 'Use RenderResponseStream()';
|
|
|
|
|
procedure RenderResponseStream; virtual;
|
2016-09-29 18:17:12 +02:00
|
|
|
|
procedure RenderWrappedList(aList: IWrappedList;
|
|
|
|
|
aJSONObjectActionProc: TJSONObjectActionProc = nil;
|
|
|
|
|
aSerializationType: TDMVCSerializationType = TDMVCSerializationType.
|
|
|
|
|
Properties);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure Render<T: class>(aCollection: TObjectList<T>;
|
|
|
|
|
aInstanceOwner: Boolean = true;
|
|
|
|
|
aJSONObjectActionProc: TJSONObjectActionProc = nil;
|
|
|
|
|
aSerializationType: TDMVCSerializationType = TDMVCSerializationType.
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Properties); overload;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure Render(aObject: TObject; aInstanceOwner: Boolean = true;
|
|
|
|
|
aSerializationType: TDMVCSerializationType = TDMVCSerializationType.
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Properties); overload; virtual;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure Render(aDataSet: TDataSet; aInstanceOwner: Boolean = false;
|
|
|
|
|
aOnlySingleRecord: Boolean = false;
|
|
|
|
|
aJSONObjectActionProc: TJSONObjectActionProc = nil); overload; virtual;
|
|
|
|
|
procedure Render(aJSONValue: TJSONValue; aInstanceOwner: Boolean = true);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
overload; virtual;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure Render(aTextWriter: TTextWriter; aInstanceOwner: Boolean = true); overload;
|
|
|
|
|
procedure RenderListAsProperty<T: class>(const aPropertyName: string;
|
|
|
|
|
aObjectList: TObjectList<T>; aOwnsInstance: Boolean = true;
|
|
|
|
|
aJSONObjectActionProc: TJSONObjectActionProc = nil);
|
|
|
|
|
procedure RenderJSONArrayAsProperty(const aPropertyName: string;
|
2016-05-11 10:39:20 +02:00
|
|
|
|
AJSONArray: TJSONArray);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure Render(E: Exception; ErrorItems: TList<string> = nil);
|
|
|
|
|
overload; virtual;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure Render(const aErrorCode: UInt16; const aErrorMessage: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
const AErrorClassName: string = ''); overload;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure Render(const aErrorCode: UInt16; aJSONValue: TJSONValue;
|
|
|
|
|
aInstanceOwner: Boolean = true); overload;
|
|
|
|
|
procedure Render(const aErrorCode: UInt16; aObject: TObject;
|
|
|
|
|
aInstanceOwner: Boolean = true); overload;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure RenderStreamAndFree(const AStream: TStream);
|
|
|
|
|
deprecated 'Use Render(TStream,Boolean)';
|
|
|
|
|
procedure Render(const AStream: TStream;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
aInstanceOwner: Boolean = true); overload;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// messaging
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure EnqueueMessageOnTopicOrQueue(const IsQueue: Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
const ATopic: string; AJSONObject: TJSONObject;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
aOwnsInstance: Boolean = true);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
function ReceiveMessageFromTopic(const ATopic: string; ATimeout: Int64;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
var JSONObject: TJSONObject): Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// redirects
|
|
|
|
|
procedure Redirect(const URL: string);
|
|
|
|
|
// http return code
|
|
|
|
|
procedure ResponseStatusCode(const AStatusCode: UInt16;
|
|
|
|
|
AStatusText: string = '');
|
|
|
|
|
// streams and files
|
2016-10-10 16:09:43 +02:00
|
|
|
|
procedure SendStream(AStream: TStream; AOwnStream: Boolean = true;
|
|
|
|
|
ARewindStream: Boolean = false); virtual;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure SendFile(AFileName: string); virtual;
|
|
|
|
|
// filters before, after
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure OnBeforeAction(Context: TWebContext; const aActionName: string;
|
|
|
|
|
var Handled: Boolean); virtual;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure OnAfterAction(Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const aActionName: string); virtual;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
|
|
|
|
|
property Config: TMVCConfig read GetMVCConfig;
|
|
|
|
|
|
|
|
|
|
public
|
|
|
|
|
// property ViewCache: TViewCache read FViewCache write SetViewCache;
|
|
|
|
|
procedure PushJSONToView(const AModelName: string; AModel: TJSONValue);
|
|
|
|
|
procedure PushObjectToView(const AModelName: string; AModel: TObject);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure PushDataSetToView(const AModelName: string; aDataSet: TDataSet);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
constructor Create;
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TMVCControllerClass = class of TMVCController;
|
|
|
|
|
|
2016-02-29 13:48:36 +01:00
|
|
|
|
TMVCControllerDelegate = reference to function: TMVCController;
|
|
|
|
|
|
|
|
|
|
TMVCControllerRoutable = class
|
|
|
|
|
strict private
|
|
|
|
|
FClass: TMVCControllerClass;
|
|
|
|
|
FDelegate: TMVCControllerDelegate;
|
|
|
|
|
public
|
2016-03-01 22:50:32 +01:00
|
|
|
|
constructor Create(AClass: TMVCControllerClass;
|
|
|
|
|
ADelegate: TMVCControllerDelegate);
|
2016-02-29 13:48:36 +01:00
|
|
|
|
|
|
|
|
|
property &Class: TMVCControllerClass read FClass;
|
|
|
|
|
property Delegate: TMVCControllerDelegate read FDelegate;
|
|
|
|
|
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>
|
|
|
|
|
/// <param name="Context">Webcontext which contains the complete request and response of the actual call.</param>
|
|
|
|
|
/// <param name="Handled">If set to True the Request would finished. Response must be set by the implementor. Default value is False.</param>
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure OnBeforeRouting(Context: TWebContext; var Handled: Boolean);
|
2016-02-23 23:22:44 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Procedure is called before the specific controller method is called.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Context">Webcontext which contains the complete request and response of the actual call.</param>
|
|
|
|
|
/// <param name="AControllerQualifiedClassName">Qualified classname of the matching controller.</param>
|
|
|
|
|
/// <param name="AActionNAme">Method name of the matching controller method.</param>
|
|
|
|
|
/// <param name="Handled">If set to True the Request would finished. Response must be set by the implementor. Default value is False.</param>
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure OnBeforeControllerAction(Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const AControllerQualifiedClassName: string; const aActionName: string;
|
|
|
|
|
var Handled: 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>
|
|
|
|
|
/// <param name="Context">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="Handled">If set to True the Request would finished. Response must be set by the implementor. Default value is False.</param>
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure OnAfterControllerAction(Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const aActionName: string; const Handled: Boolean);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TMVCEngine = class(TComponent)
|
|
|
|
|
strict private
|
|
|
|
|
FApplicationSession: TWebApplicationSession;
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
|
FWebModule: TWebModule;
|
|
|
|
|
FSavedOnBeforeDispatch: THTTPMethodEvent;
|
|
|
|
|
FMVCConfig: TMVCConfig;
|
|
|
|
|
// FViewCache : TViewCache;
|
|
|
|
|
FMimeTypes: TDictionary<string, string>;
|
|
|
|
|
procedure SetApplicationSession(const Value: TWebApplicationSession);
|
2016-02-28 19:06:05 +01:00
|
|
|
|
procedure SetDefaultReponseHeaders(AContext: TWebContext);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
protected
|
|
|
|
|
FConfiguredSessionTimeout: Int64;
|
2016-02-29 13:48:36 +01:00
|
|
|
|
FControllers: TObjectList<TMVCControllerRoutable>;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FMiddleware: TList<IMVCMiddleware>;
|
|
|
|
|
procedure ExecuteBeforeRoutingMiddleware(Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
var Handled: Boolean);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure ExecuteBeforeControllerActionMiddleware(MVCEngine: TMVCEngine;
|
|
|
|
|
Context: TWebContext; const AControllerQualifiedClassName: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const aActionName: string; var Handled: Boolean);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure ExecuteAfterControllerActionMiddleware(Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const aActionName: string; const Handled: Boolean);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure ConfigDefaultValues; virtual;
|
|
|
|
|
procedure FixUpWebModule;
|
|
|
|
|
procedure OnBeforeDispatch(Sender: TObject; Request: TWebRequest;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Response: TWebResponse; var Handled: Boolean); virtual;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
function ExecuteAction(Sender: TObject; Request: TWebRequest;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Response: TWebResponse): Boolean; virtual;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
procedure LoadSystemControllers; virtual;
|
|
|
|
|
procedure ResponseErrorPage(E: Exception; Request: TWebRequest;
|
|
|
|
|
Response: TWebResponse); virtual;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
class procedure ClearSessionCookiesAlreadySet(aCookies: TCookieCollection);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
public
|
|
|
|
|
class function GetCurrentSession(ASessionTimeout: UInt64;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const ASessionID: string; ARaiseExceptionIfExpired: Boolean = true)
|
2015-12-02 04:14:15 +01:00
|
|
|
|
: TWebSession;
|
|
|
|
|
class function ExtractSessionIDFromWebRequest
|
|
|
|
|
(AWebRequest: TWebRequest): string;
|
|
|
|
|
constructor Create(WebModule: TWebModule;
|
2016-09-06 10:41:05 +02:00
|
|
|
|
ConfigProc: TProc<TMVCConfig> = nil; CustomLogger: ILogWriter = nil); reintroduce;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
class function SendSessionCookie(AContext: TWebContext): string; overload;
|
2016-09-27 14:22:17 +02:00
|
|
|
|
class function SendSessionCookie(AContext: TWebContext; const ASessionID: string)
|
2015-12-02 04:14:15 +01:00
|
|
|
|
: string; overload;
|
|
|
|
|
function GetSessionBySessionID(const ASessionID: string): TWebSession;
|
2016-03-01 22:50:32 +01:00
|
|
|
|
function AddController(AControllerClass: TMVCControllerClass)
|
|
|
|
|
: TMVCEngine; overload;
|
|
|
|
|
function AddController(AControllerClass: TMVCControllerClass;
|
|
|
|
|
ADelegate: TMVCControllerDelegate): TMVCEngine; overload;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
function AddMiddleware(AMiddleware: IMVCMiddleware): TMVCEngine;
|
|
|
|
|
// internal methods
|
2016-02-29 13:48:36 +01:00
|
|
|
|
function RegisteredControllers: TObjectList<TMVCControllerRoutable>;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// http return codes
|
|
|
|
|
procedure Http404(AWebContext: TWebContext);
|
2016-09-27 14:22:17 +02:00
|
|
|
|
procedure Http500(AWebContext: TWebContext; const AReasonText: string = '');
|
2015-12-02 04:14:15 +01:00
|
|
|
|
property Config: TMVCConfig read FMVCConfig; // allow a simple client code
|
2016-03-01 22:50:32 +01:00
|
|
|
|
property ApplicationSession: TWebApplicationSession read FApplicationSession
|
|
|
|
|
write SetApplicationSession;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TMVCStaticContents = class(TMVCController)
|
|
|
|
|
public
|
|
|
|
|
// [MVCPath('/static/($filename)')]
|
|
|
|
|
class procedure SendFile(AFileName, AMimeType: string;
|
|
|
|
|
Context: TWebContext);
|
|
|
|
|
class function IsStaticFile(AViewPath, AWebRequestPath: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
out ARealFileName: string): Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
class function IsScriptableFile(StaticFileName: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Config: TMVCConfig): Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2014-03-10 17:39:29 +01:00
|
|
|
|
type
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMVCConfigKey = class
|
|
|
|
|
public const
|
|
|
|
|
SessionTimeout = 'sessiontimeout';
|
|
|
|
|
DocumentRoot = 'document_root';
|
|
|
|
|
ViewPath = 'view_path';
|
|
|
|
|
DefaultContentType = 'default_content_type';
|
|
|
|
|
DefaultContentCharset = 'default_content_charset';
|
|
|
|
|
DefaultViewFileExtension = 'default_view_file_extension';
|
|
|
|
|
ISAPIPath = 'isapi_path';
|
|
|
|
|
StompServer = 'stompserver';
|
|
|
|
|
StompServerPort = 'stompserverport';
|
|
|
|
|
StompUsername = 'stompusername';
|
|
|
|
|
StompPassword = 'stomppassword';
|
|
|
|
|
Messaging = 'messaging';
|
|
|
|
|
AllowUnhandledAction = 'allow_unhandled_action'; // tristan
|
2016-02-28 19:06:05 +01:00
|
|
|
|
ServerName = 'server_name'; // tristan
|
|
|
|
|
ExposeServerSignature = 'server_signature';
|
2016-04-20 11:02:27 +02:00
|
|
|
|
IndexDocument = 'index_document';
|
2016-12-05 15:50:00 +01:00
|
|
|
|
SessionType = 'session_type';
|
2016-12-07 12:34:51 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Define a default URL for requests that don't map to a route or a file
|
|
|
|
|
/// </summary>
|
|
|
|
|
FallbackResource = 'fallback_resource';
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
2014-03-10 17:39:29 +01:00
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function IsShuttingDown: Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
procedure EnterInShutdownState;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure InternalRender(const Content: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ContentType, ContentEncoding: string; Context: TWebContext); overload;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure InternalRenderText(const AContent: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ContentType, ContentEncoding: string; Context: TWebContext);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure InternalRender(aJSONValue: TJSONValue;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ContentType, ContentEncoding: string; Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
aInstanceOwner: Boolean = true); overload;
|
2013-12-04 13:06:18 +01:00
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
implementation
|
|
|
|
|
|
|
|
|
|
uses
|
2015-12-02 04:14:15 +01:00
|
|
|
|
System.SyncObjs,
|
|
|
|
|
idglobal,
|
|
|
|
|
IdGlobalProtocols,
|
|
|
|
|
System.DateUtils,
|
|
|
|
|
System.RegularExpressions,
|
|
|
|
|
WinApi.Windows,
|
|
|
|
|
System.TypInfo,
|
|
|
|
|
System.ioutils,
|
|
|
|
|
System.StrUtils,
|
|
|
|
|
Web.Win.IsapiHTTP,
|
|
|
|
|
MVCFramework.Router,
|
|
|
|
|
MVCFramework.View,
|
|
|
|
|
IdURI,
|
2016-09-06 10:30:52 +02:00
|
|
|
|
IdStack,
|
|
|
|
|
IdHTTPWebBrokerBridge,
|
2015-12-02 04:14:15 +01:00
|
|
|
|
MVCFramework.MessagingController,
|
|
|
|
|
Web.WebReq,
|
|
|
|
|
MVCFramework.SysControllers;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const
|
|
|
|
|
ALLOWED_TYPED_ACTION_PARAMETERS_TYPES =
|
|
|
|
|
'Integer, Int64, Single, Double, Extended, Boolean, TDate, TTime, TDateTime and String';
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
type
|
2016-09-06 10:30:52 +02:00
|
|
|
|
TIdHTTPAppRequestHack = class(TIdHTTPAppRequest)
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2013-11-09 14:22:11 +01:00
|
|
|
|
threadvar ctx: TRTTIContext;
|
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;
|
|
|
|
|
// this variable is used by TInterlocked functions to handl<64>e the "shuttingdown" mode
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
{ TMVCEngine }
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
function TMVCEngine.AddController(AControllerClass: TMVCControllerClass)
|
2015-12-02 04:14:15 +01:00
|
|
|
|
: TMVCEngine;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2016-02-29 13:48:36 +01:00
|
|
|
|
Result := AddController(AControllerClass, nil);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCEngine.AddController(AControllerClass: TMVCControllerClass;
|
|
|
|
|
ADelegate: TMVCControllerDelegate): TMVCEngine;
|
|
|
|
|
begin
|
|
|
|
|
FControllers.Add(TMVCControllerRoutable.Create(AControllerClass, ADelegate));
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := Self;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-03-31 11:25:16 +02:00
|
|
|
|
function TMVCEngine.AddMiddleware(AMiddleware: IMVCMiddleware): TMVCEngine;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FMiddleware.Add(AMiddleware);
|
|
|
|
|
Result := Self;
|
2014-03-31 11:25:16 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-12-05 15:50:00 +01:00
|
|
|
|
function AddSessionToTheSessionList(const aSessionType, ASessionID: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ASessionTimeout: UInt64): TWebSession;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LSess: TWebSession;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
begin
|
2016-12-05 15:50:00 +01:00
|
|
|
|
if Trim(aSessionType) = '' then
|
|
|
|
|
begin
|
|
|
|
|
raise EMVCException.Create('Empty Session Type');
|
|
|
|
|
end;
|
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMonitor.Enter(SessionList);
|
|
|
|
|
try
|
2016-12-05 15:50:00 +01:00
|
|
|
|
LSess := TMVCSessionFactory.GetInstance.CreateNewByType(
|
|
|
|
|
aSessionType,
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ASessionID, ASessionTimeout);
|
|
|
|
|
SessionList.Add(ASessionID, LSess);
|
|
|
|
|
Result := LSess;
|
|
|
|
|
LSess.MarkAsUsed;
|
|
|
|
|
finally
|
|
|
|
|
TMonitor.Exit(SessionList);
|
|
|
|
|
end;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-09-16 23:54:54 +02:00
|
|
|
|
class procedure TMVCEngine.ClearSessionCookiesAlreadySet(
|
|
|
|
|
aCookies: TCookieCollection);
|
|
|
|
|
var
|
|
|
|
|
I: Integer;
|
|
|
|
|
lSessCookieName: string;
|
|
|
|
|
lCookie: TCookie;
|
|
|
|
|
begin
|
|
|
|
|
lSessCookieName := TMVCConstants.SESSION_TOKEN_NAME.ToLower;
|
|
|
|
|
I := 0;
|
|
|
|
|
while true do
|
|
|
|
|
begin
|
|
|
|
|
if I = aCookies.Count then
|
|
|
|
|
Break;
|
|
|
|
|
lCookie := aCookies[I];
|
2016-09-25 22:31:33 +02:00
|
|
|
|
if LowerCase(lCookie.Name) = lSessCookieName then
|
2016-09-16 23:54:54 +02:00
|
|
|
|
begin
|
|
|
|
|
aCookies.Delete(I);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
Inc(I);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
procedure TMVCEngine.ConfigDefaultValues;
|
|
|
|
|
begin
|
2016-09-06 10:30:52 +02:00
|
|
|
|
Log.Info('ENTER: Config default values', LOGGERPRO_TAG);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Config[TMVCConfigKey.SessionTimeout] := '30'; // 30 minutes
|
|
|
|
|
Config[TMVCConfigKey.DocumentRoot] := '.\www';
|
2016-12-07 12:34:51 +01:00
|
|
|
|
Config[TMVCConfigKey.FallbackResource] := '';
|
2015-12-02 04:14:15 +01:00
|
|
|
|
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.Messaging] := 'false';
|
|
|
|
|
|
|
|
|
|
Config[TMVCConfigKey.AllowUnhandledAction] := 'false'; // tristan
|
2016-02-28 19:06:05 +01:00
|
|
|
|
Config[TMVCConfigKey.ServerName] := 'DelphiMVCFramework'; // tristan
|
|
|
|
|
Config[TMVCConfigKey.ExposeServerSignature] := 'true';
|
2016-12-05 15:50:00 +01:00
|
|
|
|
Config[TMVCConfigKey.SessionType] := 'memory';
|
2015-12-02 04:14:15 +01:00
|
|
|
|
|
2016-04-20 11:02:27 +02:00
|
|
|
|
Config[TMVCConfigKey.IndexDocument] := 'index.html';
|
|
|
|
|
|
2015-12-02 04:14:15 +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);
|
|
|
|
|
|
2016-09-06 10:30:52 +02:00
|
|
|
|
Log.Info('EXIT: Config default values', LOGGERPRO_TAG);
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
constructor TMVCEngine.Create(WebModule: TWebModule;
|
2016-09-06 10:30:52 +02:00
|
|
|
|
ConfigProc: TProc<TMVCConfig>; CustomLogger: ILogWriter);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
inherited Create(WebModule);
|
|
|
|
|
WebRequestHandler.CacheConnections := true;
|
|
|
|
|
WebRequestHandler.MaxConnections := 4096;
|
|
|
|
|
FMimeTypes := TDictionary<string, string>.Create;
|
|
|
|
|
FMVCConfig := TMVCConfig.Create;
|
|
|
|
|
FWebModule := WebModule;
|
2016-03-01 22:50:32 +01:00
|
|
|
|
FControllers := TObjectList<TMVCControllerRoutable>.Create(true);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FMiddleware := TList<IMVCMiddleware>.Create;
|
|
|
|
|
// FViewCache := TViewCache.Create;
|
|
|
|
|
FixUpWebModule;
|
2016-09-06 10:30:52 +02:00
|
|
|
|
MVCFramework.Logger.SetDefaultLogger(CustomLogger);
|
|
|
|
|
// WARNING!! from now on, the logger subsystem is available
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ConfigDefaultValues;
|
2016-09-06 10:30:52 +02:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if Assigned(ConfigProc) then
|
|
|
|
|
begin
|
|
|
|
|
LogEnterMethod('Custom configuration proc');
|
2016-09-06 10:30:52 +02:00
|
|
|
|
ConfigProc(FMVCConfig);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LogExitMethod('Custom configuration proc');
|
|
|
|
|
end;
|
2016-09-06 10:30:52 +02:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LoadSystemControllers;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
destructor TMVCEngine.Destroy;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FMimeTypes.Free;
|
|
|
|
|
FMVCConfig.Free;
|
|
|
|
|
FControllers.Free;
|
|
|
|
|
FMiddleware.Free;
|
|
|
|
|
// FViewCache.Free;
|
|
|
|
|
inherited;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-02-28 19:06:05 +01:00
|
|
|
|
procedure TMVCEngine.SetDefaultReponseHeaders(AContext: TWebContext);
|
|
|
|
|
begin
|
|
|
|
|
if Config[TMVCConfigKey.ExposeServerSignature] = 'true' then
|
|
|
|
|
begin
|
|
|
|
|
AContext.Response.CustomHeaders.Values['Server'] :=
|
|
|
|
|
Config[TMVCConfigKey.ServerName];
|
|
|
|
|
end;
|
|
|
|
|
AContext.Response.RawWebResponse.Date := Now;
|
|
|
|
|
end;
|
|
|
|
|
|
2015-04-01 17:01:23 +02:00
|
|
|
|
function TMVCEngine.ExecuteAction(Sender: TObject; Request: TWebRequest;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Response: TWebResponse): Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController: TMVCController;
|
|
|
|
|
lContext: TWebContext;
|
|
|
|
|
lParamsTable: TMVCRequestParamsTable;
|
|
|
|
|
lRouter: TMVCRouter;
|
|
|
|
|
lStaticFileName: string;
|
|
|
|
|
lContentType: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
lHandled: Boolean;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lResponseContentType, lResponseContentCharset: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
lActionFormalParams: TArray<TRttiParameter>;
|
|
|
|
|
lActualParams: TArray<TValue>;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
|
2016-12-07 12:34:51 +01:00
|
|
|
|
function SendFileIfPresent(const AFileName: String): Boolean;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
begin
|
2016-12-07 12:34:51 +01:00
|
|
|
|
lStaticFileName := TPath.Combine(Config[TMVCConfigKey.DocumentRoot], AFileName);
|
2016-04-20 11:02:27 +02:00
|
|
|
|
if TFile.Exists(lStaticFileName) then
|
|
|
|
|
begin
|
2016-09-05 15:34:17 +02:00
|
|
|
|
if FMimeTypes.TryGetValue(LowerCase(ExtractFileExt(lStaticFileName)), lContentType) then
|
|
|
|
|
begin
|
|
|
|
|
lContentType := lContentType + ';charset=' + FMVCConfig
|
|
|
|
|
[TMVCConfigKey.DefaultContentCharset];
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lContentType := TMVCMimeType.APPLICATION_OCTETSTREAM;
|
2016-09-05 15:34:17 +02:00
|
|
|
|
end;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
TMVCStaticContents.SendFile(lStaticFileName, lContentType, lContext);
|
|
|
|
|
Result := true;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
Result := false;
|
|
|
|
|
end;
|
|
|
|
|
|
2016-12-07 12:34:51 +01:00
|
|
|
|
function SendDocumentIndexIfPresent: Boolean;
|
|
|
|
|
begin
|
|
|
|
|
Result := SendFileIfPresent(Config[TMVCConfigKey.IndexDocument]);
|
|
|
|
|
// lStaticFileName := TPath.Combine(Config[TMVCConfigKey.DocumentRoot],
|
|
|
|
|
// Config[TMVCConfigKey.IndexDocument]);
|
|
|
|
|
// if TFile.Exists(lStaticFileName) then
|
|
|
|
|
// begin
|
|
|
|
|
// if FMimeTypes.TryGetValue(LowerCase(ExtractFileExt(lStaticFileName)), lContentType) then
|
|
|
|
|
// begin
|
|
|
|
|
// lContentType := lContentType + ';charset=' + FMVCConfig
|
|
|
|
|
// [TMVCConfigKey.DefaultContentCharset];
|
|
|
|
|
// end
|
|
|
|
|
// else
|
|
|
|
|
// begin
|
|
|
|
|
// lContentType := TMVCMimeType.APPLICATION_OCTETSTREAM;
|
|
|
|
|
// end;
|
|
|
|
|
// TMVCStaticContents.SendFile(lStaticFileName, lContentType, lContext);
|
|
|
|
|
// Result := true;
|
|
|
|
|
// end
|
|
|
|
|
// else
|
|
|
|
|
// Result := false;
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure FillActualParamsForAction(const AContext: TWebContext;
|
2016-08-09 13:08:17 +02:00
|
|
|
|
const aActionFormalParams: TArray<TRttiParameter>; const aActionName: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
var aActualParams: TArray<TValue>);
|
|
|
|
|
var
|
|
|
|
|
lParamName: string;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
I: Integer;
|
2016-08-09 13:08:17 +02:00
|
|
|
|
lStrValue: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
lFormatSettings: TFormatSettings;
|
|
|
|
|
lWasDateTime: Boolean;
|
|
|
|
|
begin
|
|
|
|
|
if AContext.Request.GetSegmentParamsCount <> Length(aActionFormalParams) then
|
|
|
|
|
raise EMVCException.CreateFmt
|
|
|
|
|
('Paramaters count mismatch (expected %d actual %d) for action "%s"',
|
|
|
|
|
[Length(aActionFormalParams), AContext.Request.GetSegmentParamsCount, aActionName]);
|
|
|
|
|
SetLength(aActualParams, Length(aActionFormalParams));
|
2016-09-16 23:54:54 +02:00
|
|
|
|
for I := 0 to Length(aActionFormalParams) - 1 do
|
2016-06-22 17:49:16 +02:00
|
|
|
|
begin
|
2016-09-16 23:54:54 +02:00
|
|
|
|
lParamName := aActionFormalParams[I].Name;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if not AContext.Request.GetSegmentParam(lParamName, lStrValue) then
|
2016-09-25 22:24:40 +02:00
|
|
|
|
raise EMVCException.CreateFmt
|
|
|
|
|
('Invalid paramater %s for action %s (Hint: Here parameters names are case-sensitive)',
|
2016-06-22 17:49:16 +02:00
|
|
|
|
[lParamName, aActionName]);
|
2016-09-16 23:54:54 +02:00
|
|
|
|
case aActionFormalParams[I].ParamType.TypeKind of
|
2016-06-22 17:49:16 +02:00
|
|
|
|
tkInteger, tkInt64:
|
|
|
|
|
begin
|
2016-09-16 23:54:54 +02:00
|
|
|
|
aActualParams[I] := StrToInt(lStrValue);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
end;
|
|
|
|
|
tkUString:
|
|
|
|
|
begin
|
2016-09-16 23:54:54 +02:00
|
|
|
|
aActualParams[I] := lStrValue;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
end;
|
|
|
|
|
tkFloat:
|
|
|
|
|
begin
|
|
|
|
|
lWasDateTime := false;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
if aActionFormalParams[I].ParamType.QualifiedName = 'System.TDate' then
|
2016-06-22 17:49:16 +02:00
|
|
|
|
begin
|
|
|
|
|
try
|
|
|
|
|
lWasDateTime := true;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
aActualParams[I] := ISOStrToDate(lStrValue);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
except
|
|
|
|
|
raise EMVCException.CreateFmt('Invalid TDate value for param [%s]',
|
2016-09-16 23:54:54 +02:00
|
|
|
|
[aActionFormalParams[I].Name]);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
end;
|
|
|
|
|
end
|
2016-09-16 23:54:54 +02:00
|
|
|
|
else if aActionFormalParams[I].ParamType.QualifiedName = 'System.TDateTime' then
|
2016-06-22 17:49:16 +02:00
|
|
|
|
begin
|
|
|
|
|
try
|
|
|
|
|
lWasDateTime := true;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
aActualParams[I] := ISOStrToDateTime(lStrValue);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
except
|
|
|
|
|
raise EMVCException.CreateFmt('Invalid TDateTime value for param [%s]',
|
2016-09-16 23:54:54 +02:00
|
|
|
|
[aActionFormalParams[I].Name]);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
end;
|
|
|
|
|
end
|
2016-09-16 23:54:54 +02:00
|
|
|
|
else if aActionFormalParams[I].ParamType.QualifiedName = 'System.TTime' then
|
2016-06-22 17:49:16 +02:00
|
|
|
|
begin
|
|
|
|
|
try
|
|
|
|
|
lWasDateTime := true;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
aActualParams[I] := ISOStrToTime(lStrValue);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
except
|
|
|
|
|
raise EMVCException.CreateFmt('Invalid TTime value for param [%s]',
|
2016-09-16 23:54:54 +02:00
|
|
|
|
[aActionFormalParams[I].Name]);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
if not lWasDateTime then
|
|
|
|
|
begin
|
|
|
|
|
lFormatSettings.DecimalSeparator := '.';
|
2016-09-16 23:54:54 +02:00
|
|
|
|
aActualParams[I] := StrToFloat(lStrValue, lFormatSettings);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
tkEnumeration:
|
|
|
|
|
begin
|
2016-09-16 23:54:54 +02:00
|
|
|
|
if aActionFormalParams[I].ParamType.QualifiedName = 'System.Boolean' then
|
2016-06-22 17:49:16 +02:00
|
|
|
|
begin
|
|
|
|
|
if SameText(lStrValue, 'true') or SameText(lStrValue, '1') then
|
2016-09-16 23:54:54 +02:00
|
|
|
|
aActualParams[I] := true
|
2016-06-22 17:49:16 +02:00
|
|
|
|
else if SameText(lStrValue, 'false') or SameText(lStrValue, '0') then
|
2016-09-16 23:54:54 +02:00
|
|
|
|
aActualParams[I] := false
|
2016-06-22 17:49:16 +02:00
|
|
|
|
else
|
|
|
|
|
raise EMVCException.CreateFmt
|
|
|
|
|
('Invalid boolean value for parameter %s. Boolean parameters accepts only "true"/"false" or "1"/"0".',
|
|
|
|
|
[lParamName]);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
raise EMVCException.CreateFmt
|
|
|
|
|
('Invalid type for parameter %s. Allowed types are ' +
|
|
|
|
|
ALLOWED_TYPED_ACTION_PARAMETERS_TYPES,
|
|
|
|
|
[lParamName]);
|
|
|
|
|
end;
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
raise EMVCException.CreateFmt
|
|
|
|
|
('Invalid type for parameter %s. Allowed types are ' +
|
|
|
|
|
ALLOWED_TYPED_ACTION_PARAMETERS_TYPES,
|
|
|
|
|
[lParamName]);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
tkChar, tkEnumeration, ,
|
|
|
|
|
tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,
|
|
|
|
|
tkVariant, tkArray, tkRecord, tkInterface, , tkDynArray, tkUString,
|
|
|
|
|
tkClassRef, tkPointer, tkProcedure }
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
// LogEnterMethod(Request.PathInfo);
|
|
|
|
|
// try
|
|
|
|
|
Result := false;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lParamsTable := TMVCRequestParamsTable.Create;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
try
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lContext := TWebContext.Create(Request, Response, FMVCConfig);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
try
|
2016-04-20 11:02:27 +02:00
|
|
|
|
SetDefaultReponseHeaders(lContext); // tristan
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// Static file handling
|
2016-10-10 15:48:48 +02:00
|
|
|
|
if (not FMVCConfig[TMVCConfigKey.DocumentRoot].IsEmpty) and
|
|
|
|
|
// dt: if document_root is empty, no static file are served
|
|
|
|
|
(TMVCStaticContents.IsStaticFile(TPath.Combine(AppPath,
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FMVCConfig[TMVCConfigKey.DocumentRoot]), Request.PathInfo,
|
2016-10-10 15:48:48 +02:00
|
|
|
|
lStaticFileName)) then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
// if TMVCStaticContents.IsScriptableFile(StaticFileName, FMVCConfig) then
|
|
|
|
|
// // execute the file
|
|
|
|
|
// begin
|
|
|
|
|
// ExecuteFile(StaticFileName, Context);
|
|
|
|
|
// end
|
|
|
|
|
// else // serve the file
|
|
|
|
|
// begin
|
2016-09-05 15:34:17 +02:00
|
|
|
|
if FMimeTypes.TryGetValue(LowerCase(ExtractFileExt(lStaticFileName)), lContentType) then
|
|
|
|
|
begin
|
|
|
|
|
lContentType := lContentType + ';charset=' + FMVCConfig
|
|
|
|
|
[TMVCConfigKey.DefaultContentCharset];
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lContentType := TMVCMimeType.APPLICATION_OCTETSTREAM;
|
2016-09-05 15:34:17 +02:00
|
|
|
|
end;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
TMVCStaticContents.SendFile(lStaticFileName, lContentType, lContext);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := true;
|
|
|
|
|
// end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lRouter := TMVCRouter.Create(Config);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
try
|
2016-04-20 11:02:27 +02:00
|
|
|
|
ExecuteBeforeRoutingMiddleware(lContext, lHandled);
|
|
|
|
|
if not lHandled then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-04-20 11:02:27 +02:00
|
|
|
|
if lRouter.ExecuteRouting(Request.PathInfo,
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMVCRouter.StringMethodToHTTPMetod(Request.Method),
|
|
|
|
|
Request.ContentType, Request.Accept, FControllers,
|
|
|
|
|
FMVCConfig[TMVCConfigKey.DefaultContentType],
|
2016-04-20 11:02:27 +02:00
|
|
|
|
FMVCConfig[TMVCConfigKey.DefaultContentCharset], lParamsTable,
|
|
|
|
|
lResponseContentType, lResponseContentCharset) then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-04-20 11:02:27 +02:00
|
|
|
|
if Assigned(lRouter.MVCControllerDelegate) then
|
|
|
|
|
lSelectedController := lRouter.MVCControllerDelegate()
|
2016-02-29 13:48:36 +01:00
|
|
|
|
else
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController := lRouter.MVCControllerClass.Create;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
try
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.SetMVCConfig(Config);
|
|
|
|
|
lSelectedController.ApplicationSession := FApplicationSession;
|
|
|
|
|
lContext.SetParams(lParamsTable);
|
|
|
|
|
lSelectedController.SetContext(lContext);
|
|
|
|
|
lSelectedController.SetMVCEngine(Self);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
|
|
|
|
|
// exception?
|
|
|
|
|
try
|
|
|
|
|
{ middlewares before controller action }
|
2016-04-20 11:02:27 +02:00
|
|
|
|
ExecuteBeforeControllerActionMiddleware(Self, lContext,
|
|
|
|
|
lRouter.MVCControllerClass.QualifiedClassName,
|
|
|
|
|
lRouter.MethodToCall.Name, lHandled);
|
|
|
|
|
if lHandled then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Exit(true);
|
|
|
|
|
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.MVCControllerAfterCreate;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
try
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lHandled := false;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// gets response contentype from MVCProduces attribute
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.ContentType := lResponseContentType;
|
|
|
|
|
lSelectedController.ContentCharset :=
|
|
|
|
|
lResponseContentCharset;
|
|
|
|
|
if not lHandled then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
|
|
|
|
|
lActionFormalParams := lRouter.MethodToCall.GetParameters;
|
|
|
|
|
|
|
|
|
|
// case1: check for parameterless action
|
|
|
|
|
if Length(lActionFormalParams) = 0 then
|
|
|
|
|
begin
|
2016-09-06 08:37:54 +02:00
|
|
|
|
SetLength(lActualParams, 0);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
end
|
|
|
|
|
// case2: check for action with only TWebContext
|
|
|
|
|
else if (Length(lActionFormalParams) = 1) and
|
|
|
|
|
(SameText(lActionFormalParams[0].ParamType.QualifiedName,
|
|
|
|
|
'MVCFramework.TWebContext')) then
|
|
|
|
|
begin
|
2016-09-06 08:37:54 +02:00
|
|
|
|
SetLength(lActualParams, 1);
|
|
|
|
|
lActualParams[0] := lContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
end
|
|
|
|
|
// case3: strongly typed declaration... injection parameters
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
FillActualParamsForAction(lContext, lActionFormalParams,
|
|
|
|
|
lRouter.MethodToCall.Name, lActualParams);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
lSelectedController.OnBeforeAction(lContext,
|
|
|
|
|
lRouter.MethodToCall.Name, lHandled);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
try
|
2016-06-22 17:49:16 +02:00
|
|
|
|
lRouter.MethodToCall.Invoke(lSelectedController, lActualParams);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
finally
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.OnAfterAction(lContext,
|
|
|
|
|
lRouter.MethodToCall.Name);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
finally
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.MVCControllerBeforeDestroy;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
ExecuteAfterControllerActionMiddleware(lContext,
|
|
|
|
|
lRouter.MethodToCall.Name, lHandled);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
except
|
|
|
|
|
on E: EMVCSessionExpiredException do
|
|
|
|
|
begin
|
|
|
|
|
LogException(E, E.DetailedMessage);
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lContext.SessionStop(false);
|
|
|
|
|
lSelectedController.ResponseStatusCode(E.HTTPErrorCode);
|
|
|
|
|
lSelectedController.Render(E);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
on E: EMVCException do
|
|
|
|
|
begin
|
|
|
|
|
LogException(E, E.DetailedMessage);
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.ResponseStatusCode(E.HTTPErrorCode);
|
|
|
|
|
lSelectedController.Render(E);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
on E: EInvalidOp do
|
|
|
|
|
begin
|
|
|
|
|
LogException(E, 'Invalid OP');
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.Render(E);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
on E: Exception do
|
|
|
|
|
begin
|
|
|
|
|
LogException(E, 'Global Action Exception Handler');
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.Render(E);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lContext.Response.ContentType :=
|
|
|
|
|
lSelectedController.ContentType;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
|
|
|
|
|
Log(TLogLevel.levNormal, Request.Method + ':' +
|
|
|
|
|
Request.RawPathInfo + ' -> ' +
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lRouter.MVCControllerClass.QualifiedClassName + ' - ' +
|
2016-02-29 13:03:52 +01:00
|
|
|
|
IntToStr(Response.StatusCode) + ' ' + Response.ReasonString)
|
2015-12-02 04:14:15 +01:00
|
|
|
|
finally
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lSelectedController.Free;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
if Config[TMVCConfigKey.AllowUnhandledAction] = 'false' then
|
|
|
|
|
begin
|
2016-12-07 12:34:51 +01:00
|
|
|
|
Result := false;
|
|
|
|
|
if not Config[TMVCConfigKey.FallbackResource].IsEmpty then
|
|
|
|
|
Result := SendFileIfPresent(Config[TMVCConfigKey.FallbackResource]);
|
|
|
|
|
if not Result then
|
2016-09-05 15:34:17 +02:00
|
|
|
|
begin
|
|
|
|
|
Http404(lContext);
|
|
|
|
|
Log(TLogLevel.levNormal, Request.Method + ':' +
|
|
|
|
|
Request.RawPathInfo + ' -> NO ACTION ' + ' - ' +
|
|
|
|
|
IntToStr(Response.StatusCode) + ' ' +
|
|
|
|
|
Response.ReasonString);
|
|
|
|
|
end;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2016-09-05 15:34:17 +02:00
|
|
|
|
Result := false;
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lContext.Response.FlushOnDestroy := false; // tristan
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
finally
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lRouter.Free;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end; // end if IS_STATIC
|
|
|
|
|
finally
|
|
|
|
|
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lContext.Free;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
finally
|
2016-04-20 11:02:27 +02:00
|
|
|
|
lParamsTable.Free;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
// finally
|
|
|
|
|
// LogExitMethod(Request.PathInfo + ' [' + IntToStr(Response.StatusCode) + ' ' +
|
|
|
|
|
// Response.ReasonString + ']');
|
|
|
|
|
// end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCEngine.ExecuteAfterControllerActionMiddleware
|
2016-06-22 17:49:16 +02:00
|
|
|
|
(Context: TWebContext; const aActionName: string; const Handled: Boolean);
|
2014-03-31 11:25:16 +02:00
|
|
|
|
var
|
2016-10-02 17:44:20 +02:00
|
|
|
|
I: Integer;
|
2014-03-31 11:25:16 +02:00
|
|
|
|
begin
|
2016-10-02 17:44:20 +02:00
|
|
|
|
for I := FMiddleware.Count - 1 downto 0 do
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-10-02 17:44:20 +02:00
|
|
|
|
FMiddleware[I].OnAfterControllerAction(Context, aActionName, Handled);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
2014-03-31 11:25:16 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCEngine.ExecuteBeforeControllerActionMiddleware
|
2015-12-02 04:14:15 +01:00
|
|
|
|
(MVCEngine: TMVCEngine; Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const AControllerQualifiedClassName: string; const aActionName: string;
|
|
|
|
|
var Handled: Boolean);
|
2015-04-01 17:01:23 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LMiddleware: IMVCMiddleware;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Handled then
|
|
|
|
|
for LMiddleware in FMiddleware do
|
|
|
|
|
begin
|
|
|
|
|
LMiddleware.OnBeforeControllerAction(Context,
|
2016-06-22 17:49:16 +02:00
|
|
|
|
AControllerQualifiedClassName, aActionName, Handled);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if Handled then
|
2016-09-16 23:54:54 +02:00
|
|
|
|
Break;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCEngine.ExecuteBeforeRoutingMiddleware(Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
var Handled: Boolean);
|
2014-03-31 11:25:16 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
middleware: IMVCMiddleware;
|
2014-03-31 11:25:16 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Handled then
|
|
|
|
|
for middleware in FMiddleware do
|
|
|
|
|
begin
|
|
|
|
|
middleware.OnBeforeRouting(Context, Handled);
|
|
|
|
|
if Handled then
|
2016-09-16 23:54:54 +02:00
|
|
|
|
Break;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
2014-03-31 11:25:16 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
function TMVCEngine.ExtractSessionIDFromWebRequest
|
2015-12-02 04:14:15 +01:00
|
|
|
|
(AWebRequest: TWebRequest): string;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +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;
|
|
|
|
|
|
2015-04-01 17:01:23 +02:00
|
|
|
|
procedure TMVCEngine.FixUpWebModule;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FSavedOnBeforeDispatch := FWebModule.BeforeDispatch;
|
|
|
|
|
FWebModule.BeforeDispatch := OnBeforeDispatch;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
function TMVCEngine.GetCurrentSession(ASessionTimeout: UInt64;
|
|
|
|
|
const ASessionID: string; ARaiseExceptionIfExpired: Boolean): TWebSession;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// SessionID: string;
|
|
|
|
|
List: TObjectDictionary<string, TWebSession>;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
IsExpired: Boolean;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
List := SessionList;
|
|
|
|
|
TMonitor.Enter(List);
|
|
|
|
|
try
|
|
|
|
|
Result := nil;
|
|
|
|
|
|
|
|
|
|
// if ASessionID.IsEmpty then
|
|
|
|
|
// raise EMVCException.Create('Empty SessionID');
|
|
|
|
|
|
|
|
|
|
{ SESSION IS NOT AUTOCREATED BY DEFAULT }
|
|
|
|
|
if not ASessionID.IsEmpty then
|
|
|
|
|
begin
|
|
|
|
|
IsExpired := true;
|
|
|
|
|
if List.TryGetValue(ASessionID, Result) then
|
|
|
|
|
begin
|
2016-02-28 19:06:05 +01:00
|
|
|
|
IsExpired := MinutesBetween(Now, Result.LastAccess) > ASessionTimeout;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// StrToInt(Config.Value['sessiontimeout']);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
if Assigned(Result) then
|
|
|
|
|
begin
|
|
|
|
|
if IsExpired then
|
|
|
|
|
begin
|
|
|
|
|
List.Remove(ASessionID); // 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;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCEngine.GetSessionBySessionID(const ASessionID: string)
|
2015-12-02 04:14:15 +01:00
|
|
|
|
: TWebSession;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := TMVCEngine.GetCurrentSession
|
|
|
|
|
(StrToInt64(Config[TMVCConfigKey.SessionTimeout]), ASessionID, false);
|
|
|
|
|
if Assigned(Result) then
|
|
|
|
|
begin
|
|
|
|
|
Result.MarkAsUsed;
|
|
|
|
|
// TMVCEngine.SendSessionCookie(FContext, SessionID);
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCEngine.Http404(AWebContext: TWebContext);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
AWebContext.Response.StatusCode := 404;
|
|
|
|
|
AWebContext.Response.ReasonString := 'Not Found';
|
|
|
|
|
AWebContext.Response.Content := 'Not Found';
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-09-27 14:22:17 +02:00
|
|
|
|
procedure TMVCEngine.Http500(AWebContext: TWebContext; const AReasonText: string);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
AWebContext.Response.StatusCode := 500;
|
|
|
|
|
AWebContext.Response.ReasonString := 'Internal server error: ' + AReasonText;
|
|
|
|
|
AWebContext.Response.Content := 'Internal server error: ' + AReasonText;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCEngine.LoadSystemControllers;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Log(TLogLevel.levNormal, 'ENTER: LoadSystemControllers');
|
|
|
|
|
AddController(TMVCSystemController);
|
|
|
|
|
if Config[TMVCConfigKey.Messaging].ToLower.Equals('true') then
|
|
|
|
|
begin
|
|
|
|
|
AddController(TMVCBUSController);
|
|
|
|
|
Log(TLogLevel.levNormal, 'Loaded system controller ' +
|
|
|
|
|
TMVCBUSController.QualifiedClassName);
|
|
|
|
|
end;
|
|
|
|
|
Log(TLogLevel.levNormal, 'EXIT: LoadSystemControllers');
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCEngine.OnBeforeDispatch(Sender: TObject; Request: TWebRequest;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Response: TWebResponse; var Handled: Boolean);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
Handled := false;
|
|
|
|
|
if Assigned(FSavedOnBeforeDispatch) then
|
|
|
|
|
FSavedOnBeforeDispatch(Sender, Request, Response, Handled);
|
|
|
|
|
// _Request := Request as TIdHTTPAppRequest;
|
|
|
|
|
if not Handled then
|
|
|
|
|
begin
|
|
|
|
|
try
|
|
|
|
|
// "X-Requested-With", "XMLHttpRequest"
|
|
|
|
|
Handled := ExecuteAction(Sender, Request, Response); // tristan
|
|
|
|
|
except
|
|
|
|
|
on E: Exception do
|
|
|
|
|
begin
|
|
|
|
|
LogException(E);
|
|
|
|
|
// Response.ContentStream.Size := 0;
|
|
|
|
|
Response.Content := E.Message;
|
|
|
|
|
Response.SendResponse;
|
|
|
|
|
Handled := true;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
// Handled := true;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-02-29 13:48:36 +01:00
|
|
|
|
function TMVCEngine.RegisteredControllers: TObjectList<TMVCControllerRoutable>;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FControllers;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCEngine.ResponseErrorPage(E: Exception; Request: TWebRequest;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Response: TWebResponse);
|
|
|
|
|
begin
|
|
|
|
|
Response.SetCustomHeader('x-mvc-error', E.ClassName + ': ' + E.Message);
|
|
|
|
|
Response.StatusCode := 200;
|
2016-10-02 17:44:20 +02:00
|
|
|
|
// if Pos('text/html', LowerCase(Request.Accept)) = 1 then
|
|
|
|
|
// begin
|
|
|
|
|
// Response.ContentType := 'text/plain';
|
|
|
|
|
// Response.Content := Config[TMVCConfigKey.ServerName] + ' ERROR:' +
|
|
|
|
|
// sLineBreak + 'Exception raised of class: ' + E.ClassName + sLineBreak +
|
|
|
|
|
// '***********************************************' + sLineBreak + E.Message
|
|
|
|
|
// + sLineBreak + '***********************************************';
|
|
|
|
|
// end
|
|
|
|
|
// else
|
|
|
|
|
// Same code in if and else section
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
Response.ContentType := 'text/plain';
|
2016-02-28 19:06:05 +01:00
|
|
|
|
Response.Content := Config[TMVCConfigKey.ServerName] + ' ERROR:' +
|
|
|
|
|
sLineBreak + 'Exception raised of class: ' + E.ClassName + sLineBreak +
|
2015-12-02 04:14:15 +01:00
|
|
|
|
'***********************************************' + sLineBreak + E.Message
|
|
|
|
|
+ sLineBreak + '***********************************************';
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
function TMVCEngine.SendSessionCookie(AContext: TWebContext): string;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LSessionID: string;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LSessionID := StringReplace
|
|
|
|
|
(StringReplace(StringReplace(GUIDToString(TGUID.NewGuid), '}', '', []), '{',
|
|
|
|
|
'', []), '-', '', [rfReplaceAll]);
|
|
|
|
|
Result := SendSessionCookie(AContext, LSessionID);
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
function TMVCEngine.SendSessionCookie(AContext: TWebContext;
|
2016-09-27 14:22:17 +02:00
|
|
|
|
const ASessionID: string): string;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Cookie: TCookie;
|
2016-02-28 18:35:50 +01:00
|
|
|
|
LSessTimeout: Integer;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
begin
|
2016-09-16 23:54:54 +02:00
|
|
|
|
ClearSessionCookiesAlreadySet(AContext.Response.Cookies);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Cookie := AContext.Response.Cookies.Add;
|
|
|
|
|
Cookie.Name := TMVCConstants.SESSION_TOKEN_NAME;
|
|
|
|
|
Cookie.Value := ASessionID;
|
2016-02-28 18:35:50 +01:00
|
|
|
|
LSessTimeout := StrToIntDef(AContext.Config[TMVCConfigKey.SessionTimeout], 0);
|
|
|
|
|
if LSessTimeout = 0 then
|
|
|
|
|
Cookie.Expires := 0
|
|
|
|
|
else
|
2016-02-28 19:06:05 +01:00
|
|
|
|
Cookie.Expires := Now + OneMinute * LSessTimeout;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Cookie.Path := '/';
|
|
|
|
|
Result := ASessionID;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
procedure TMVCEngine.SetApplicationSession(const Value: TWebApplicationSession);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FApplicationSession := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ TWebContext }
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
constructor TWebContext.Create(ARequest: TWebRequest; AResponse: TWebResponse;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
AConfig: TMVCConfig);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited Create;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
FIsSessionStarted := false;
|
|
|
|
|
FSessionMustBeClose := false;
|
2014-05-14 15:55:41 +02:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if IsLibrary then
|
|
|
|
|
begin
|
2014-09-29 17:42:34 +02:00
|
|
|
|
{$IF CompilerVersion >= 27}
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if ARequest is TApacheRequest then
|
|
|
|
|
FRequest := TMVCApacheWebRequest.Create(ARequest)
|
|
|
|
|
else if ARequest is TISAPIRequest then
|
|
|
|
|
FRequest := TMVCISAPIWebRequest.Create(ARequest)
|
|
|
|
|
else
|
|
|
|
|
raise EMVCException.Create('Unknown request type ' + ARequest.ClassName);
|
2014-05-14 15:55:41 +02:00
|
|
|
|
{$ELSE}
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FRequest := TMVCISAPIWebRequest.Create(ARequest)
|
2014-05-14 15:55:41 +02:00
|
|
|
|
{$ENDIF}
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
FRequest := TMVCINDYWebRequest.Create(ARequest);
|
|
|
|
|
end;
|
|
|
|
|
FResponse := TMVCWebResponse.Create(AResponse);
|
|
|
|
|
FConfig := AConfig;
|
|
|
|
|
FData := TDictionary<string, string>.Create;
|
|
|
|
|
FLoggedUser := TUser.Create;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
destructor TWebContext.Destroy;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FreeAndNil(FResponse);
|
|
|
|
|
FreeAndNil(FRequest);
|
|
|
|
|
FreeAndNil(FData);
|
|
|
|
|
FreeAndNil(FLoggedUser);
|
2016-04-03 22:35:27 +02:00
|
|
|
|
// do not destroy session here... it is stored in the session list
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TWebContext.Flush;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FResponse.Flush;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-05-21 17:16:15 +02:00
|
|
|
|
function TWebContext.GetData: TDictionary<string, string>;
|
2014-03-31 11:25:16 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FData;
|
2014-03-31 11:25:16 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2015-04-01 17:01:23 +02:00
|
|
|
|
function TWebContext.GetLoggedUser: TUser;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Assigned(FLoggedUser) then
|
|
|
|
|
begin
|
|
|
|
|
FLoggedUser := TUser.Create;
|
|
|
|
|
end;
|
|
|
|
|
Result := FLoggedUser;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-04-03 22:35:27 +02:00
|
|
|
|
function TWebContext.GetWebSession: TWebSession;
|
|
|
|
|
begin
|
|
|
|
|
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
|
|
|
|
|
begin
|
|
|
|
|
TMVCEngine.SendSessionCookie(Self, FWebSession.SessionID);
|
|
|
|
|
// daniele
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
Result := FWebSession;
|
|
|
|
|
Result.MarkAsUsed;
|
|
|
|
|
{
|
|
|
|
|
LSessionIDFromWebRequest := TMVCEngine.ExtractSessionIDFromWebRequest
|
|
|
|
|
(Context.Request.RawWebRequest);
|
|
|
|
|
LWebSession := TMVCEngine.GetCurrentSession
|
|
|
|
|
(Context.Config.AsInt64[TMVCConfigKey.SessionTimeout],
|
|
|
|
|
LSessionIDFromWebRequest, False);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TWebContext.IsSessionStarted: Boolean;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
begin
|
|
|
|
|
Result := FIsSessionStarted;
|
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
procedure TWebContext.SetParams(AParamsTable: TMVCRequestParamsTable);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FParamsTable := AParamsTable;
|
|
|
|
|
FRequest.FParamsTable := AParamsTable;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ TMVCWebResponse }
|
|
|
|
|
|
|
|
|
|
constructor TMVCWebResponse.Create(AWebResponse: TWebResponse);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FStreamOutputDone := false;
|
|
|
|
|
FFlushOnDestroy := true;
|
|
|
|
|
inherited Create;
|
|
|
|
|
FWebResponse := AWebResponse;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
destructor TMVCWebResponse.Destroy;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if FFlushOnDestroy then // tristan
|
|
|
|
|
Flush;
|
|
|
|
|
inherited;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCWebResponse.Flush;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
try
|
|
|
|
|
FWebResponse.SendResponse; // daniele
|
|
|
|
|
except
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebResponse.GetContent: string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebResponse.Content;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebResponse.GetContentType: string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebResponse.ContentType;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebResponse.GetCookies: TCookieCollection;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := Self.FWebResponse.Cookies;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebResponse.GetCustomHeaders: TStrings;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebResponse.CustomHeaders;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-05-21 17:16:15 +02:00
|
|
|
|
function TMVCWebResponse.GetLocation: string;
|
2014-03-25 12:41:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := CustomHeaders.Values['location'];
|
2014-03-25 12:41:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-08-27 11:13:40 +02:00
|
|
|
|
function TMVCWebResponse.GetReasonString: string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebResponse.ReasonString;
|
2015-08-27 11:13:40 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
function TMVCWebResponse.GetStatusCode: Integer;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebResponse.StatusCode;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCWebResponse.SendHeaders;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FWebResponse.SendResponse
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCWebResponse.SetContent(const Value: string);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FWebResponse.Content := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCWebResponse.SetContentStream(AStream: TStream;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
AContentType: string);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FWebResponse.ContentType := AContentType;
|
|
|
|
|
FWebResponse.ContentStream := AStream;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCWebResponse.SetContentType(const Value: string);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FWebResponse.ContentType := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCWebResponse.SetCustomHeader(const Name, Value: string);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Self.FWebResponse.SetCustomHeader(name, Value);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-05-21 17:16:15 +02:00
|
|
|
|
procedure TMVCWebResponse.SetLocation(const Value: string);
|
2014-03-25 12:41:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
CustomHeaders.Values['location'] := Value;
|
2014-03-25 12:41:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
procedure TMVCWebResponse.SetReasonString(const Value: string);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FWebResponse.ReasonString := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCWebResponse.SetStatusCode(const Value: Integer);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FWebResponse.StatusCode := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ TMVCWebRequest }
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.Accept: string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := Self.FWebRequest.Accept;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.Body: string;
|
2016-12-13 18:16:09 +01:00
|
|
|
|
{ .$IF CompilerVersion <= 27 }
|
2014-05-05 18:52:49 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
InEnc: TEncoding;
|
|
|
|
|
Buffer: TArray<Byte>;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
I: Integer;
|
2016-12-13 18:16:09 +01:00
|
|
|
|
{ .$ENDIF }
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-09-20 11:10:40 +02:00
|
|
|
|
if FBody <> '' then
|
|
|
|
|
Exit(FBody);
|
2016-12-13 18:16:09 +01:00
|
|
|
|
{ .$IF CompilerVersion > 29 }
|
|
|
|
|
// FWebRequest.ReadTotalContent;
|
|
|
|
|
// Exit(FWebRequest.Content);
|
|
|
|
|
{ .$ELSE }
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// 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
|
2016-12-13 18:16:09 +01:00
|
|
|
|
FWebRequest.ReadTotalContent;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if FCharset.IsEmpty then
|
|
|
|
|
begin
|
|
|
|
|
SetLength(Buffer, 10);
|
2016-09-16 23:54:54 +02:00
|
|
|
|
for I := 0 to 9 do
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-09-16 23:54:54 +02:00
|
|
|
|
Buffer[I] := Byte(FWebRequest.RawContent[I]);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
TEncoding.GetBufferEncoding(Buffer, InEnc, TEncoding.Default);
|
|
|
|
|
SetLength(Buffer, 0);
|
|
|
|
|
end
|
|
|
|
|
else
|
2015-12-16 16:41:06 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
InEnc := TEncoding.GetEncoding(FCharset);
|
2015-12-16 16:41:06 +01:00
|
|
|
|
end;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
try
|
2016-12-13 18:16:09 +01:00
|
|
|
|
// SetLength(Buffer, FWebRequest.ContentLength);
|
|
|
|
|
// FWebRequest.RawContent
|
|
|
|
|
// FWebRequest.ReadClient(Buffer[0], FWebRequest.ContentLength);
|
|
|
|
|
FBody := InEnc.GetString(FWebRequest.RawContent);
|
2016-09-20 11:10:40 +02:00
|
|
|
|
Result := FBody;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
finally
|
|
|
|
|
InEnc.Free;
|
|
|
|
|
end
|
2016-12-13 18:16:09 +01: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;
|
2013-11-18 00:16:59 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
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
|
|
|
|
|
Result := Mapper.JSONObjectToObject<T>(BodyAsJSONObject.Get(S)
|
|
|
|
|
.JsonValue as TJSONObject)
|
|
|
|
|
else
|
|
|
|
|
raise EMVCException.CreateFmt('Body property %s not valid',
|
|
|
|
|
[RootProperty]);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
raise EMVCException.CreateFmt('Body ContentType %s not supported',
|
|
|
|
|
[ContentType]);
|
2013-11-18 00:16:59 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
function TMVCWebRequest.BodyAsJSONObject: TJSONObject;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := BodyAsJSONValue as TJSONObject;
|
2013-11-09 14:22:11 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.BodyAsJSONValue: TJSONValue;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Assigned(FBodyAsJSONValue) then
|
|
|
|
|
try
|
|
|
|
|
FBodyAsJSONValue := TJSONObject.ParseJSONValue(Body);
|
|
|
|
|
except
|
|
|
|
|
FBodyAsJSONValue := nil;
|
|
|
|
|
end;
|
|
|
|
|
Result := FBodyAsJSONValue;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
function TMVCWebRequest.BodyAsListOf<T>(const RootProperty: string)
|
2015-12-02 04:14:15 +01:00
|
|
|
|
: TObjectList<T>;
|
2013-11-18 00:16:59 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
S: string;
|
|
|
|
|
begin
|
|
|
|
|
if ContentType.Equals(TMVCMimeType.APPLICATION_JSON) then
|
|
|
|
|
begin
|
|
|
|
|
if RootProperty = '' then
|
|
|
|
|
Result := Mapper.JSONArrayToObjectList<T>((BodyAsJSONValue as TJSONArray),
|
|
|
|
|
false, true)
|
|
|
|
|
// Ezequiel J. M<>ller (bug fix)
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
S := Mapper.GetStringDef(BodyAsJSONObject, RootProperty, '');
|
|
|
|
|
if not S.IsEmpty then
|
|
|
|
|
Result := Mapper.JSONArrayToObjectList<T>(BodyAsJSONObject.Get(S)
|
|
|
|
|
.JsonValue as TJSONArray, false, true) // thank you Ezequiel J. M<>ller
|
|
|
|
|
else
|
|
|
|
|
raise EMVCException.CreateFmt('Body property %s not valid',
|
|
|
|
|
[RootProperty]);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
raise EMVCException.CreateFmt('Body ContentType %s not supported',
|
|
|
|
|
[ContentType]);
|
2013-11-18 00:16:59 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-12-16 15:57:20 +01:00
|
|
|
|
function TMVCWebRequest.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/
|
|
|
|
|
}
|
|
|
|
|
var
|
|
|
|
|
S: string;
|
|
|
|
|
begin
|
|
|
|
|
if FWebRequest.GetFieldByName('HTTP_CLIENT_IP') <> '' then
|
|
|
|
|
Exit(FWebRequest.GetFieldByName('HTTP_CLIENT_IP'));
|
|
|
|
|
|
2016-08-09 13:08:17 +02:00
|
|
|
|
for S in string(FWebRequest.GetFieldByName('HTTP_X_FORWARDED_FOR'))
|
2015-12-16 15:57:20 +01:00
|
|
|
|
.Split([',']) do
|
|
|
|
|
begin
|
2016-12-05 15:50:00 +01:00
|
|
|
|
if not S.Trim.IsEmpty then
|
|
|
|
|
Exit(S.Trim);
|
2015-12-16 15:57:20 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
if FWebRequest.GetFieldByName('HTTP_X_FORWARDED') <> '' then
|
|
|
|
|
Exit(FWebRequest.GetFieldByName('HTTP_X_FORWARDED'));
|
|
|
|
|
|
|
|
|
|
if FWebRequest.GetFieldByName('HTTP_X_CLUSTER_CLIENT_IP') <> '' then
|
|
|
|
|
Exit(FWebRequest.GetFieldByName('HTTP_X_CLUSTER_CLIENT_IP'));
|
|
|
|
|
|
|
|
|
|
if FWebRequest.GetFieldByName('HTTP_FORWARDED_FOR') <> '' then
|
|
|
|
|
Exit(FWebRequest.GetFieldByName('HTTP_FORWARDED_FOR'));
|
|
|
|
|
|
|
|
|
|
if FWebRequest.GetFieldByName('HTTP_FORWARDED') <> '' then
|
|
|
|
|
Exit(FWebRequest.GetFieldByName('HTTP_FORWARDED'));
|
|
|
|
|
|
|
|
|
|
if FWebRequest.GetFieldByName('REMOTE_ADDR') <> '' then
|
|
|
|
|
Exit(FWebRequest.GetFieldByName('REMOTE_ADDR'));
|
|
|
|
|
|
|
|
|
|
if FWebRequest.RemoteIP <> '' then
|
|
|
|
|
Exit(FWebRequest.RemoteIP);
|
|
|
|
|
|
|
|
|
|
if FWebRequest.RemoteAddr <> '' then
|
|
|
|
|
Exit(FWebRequest.RemoteAddr);
|
|
|
|
|
|
|
|
|
|
if FWebRequest.RemoteHost <> '' then
|
|
|
|
|
Exit(FWebRequest.RemoteHost);
|
|
|
|
|
|
|
|
|
|
if FWebRequest.RemoteAddr <> '' then
|
|
|
|
|
Exit(FWebRequest.RemoteAddr);
|
|
|
|
|
|
|
|
|
|
if FWebRequest.RemoteIP <> '' then
|
|
|
|
|
Exit(FWebRequest.RemoteIP);
|
|
|
|
|
|
|
|
|
|
if FWebRequest.RemoteHost <> '' then
|
|
|
|
|
Exit(FWebRequest.RemoteHost);
|
|
|
|
|
|
|
|
|
|
Result := '';
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TMVCWebRequest.ClientPrefer(MimeType: string): Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := AnsiPos(MimeType, LowerCase(RawWebRequest.Accept)) = 1;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.ContentParam(Name: string): string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebRequest.ContentFields.Values[name];
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.Cookie(Name: string): string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebRequest.CookieFields.Values[name];
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2013-11-18 00:16:59 +01:00
|
|
|
|
constructor TMVCWebRequest.Create(AWebRequest: TWebRequest);
|
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
CT: TArray<string>;
|
|
|
|
|
c: string;
|
|
|
|
|
begin
|
|
|
|
|
inherited Create;
|
2016-09-20 11:10:40 +02:00
|
|
|
|
FBody := '';
|
2015-12-02 04:14:15 +01:00
|
|
|
|
c := AWebRequest.GetFieldByName('Content-Type');
|
|
|
|
|
if not c.IsEmpty then
|
|
|
|
|
begin
|
|
|
|
|
CT := c.Split([';']);
|
2016-12-05 15:50:00 +01:00
|
|
|
|
FContentType := Trim(CT[0]);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FCharset := TMVCConstants.DEFAULT_CONTENT_CHARSET; // default charset
|
|
|
|
|
if Length(CT) > 1 then
|
|
|
|
|
begin
|
2016-12-05 15:50:00 +01:00
|
|
|
|
if CT[1].Trim.StartsWith('charset', true) then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-12-05 15:50:00 +01:00
|
|
|
|
FCharset := CT[1].Trim.Split(['='])[1].Trim;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
// c := GetHeaderValue('content-encoding');
|
|
|
|
|
// if c.IsEmpty then
|
|
|
|
|
// FContentEncoding := c;
|
2013-11-18 00:16:59 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
destructor TMVCWebRequest.Destroy;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FreeAndNil(FBodyAsJSONValue);
|
|
|
|
|
inherited;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ TMVCAction }
|
|
|
|
|
|
2016-04-03 22:35:27 +02:00
|
|
|
|
procedure TWebContext.BindToSession(SessionID: string);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Assigned(FWebSession) then
|
|
|
|
|
begin
|
|
|
|
|
FWebSession := TMVCEngine.GetCurrentSession
|
2016-04-03 22:35:27 +02:00
|
|
|
|
(StrToInt64(FConfig[TMVCConfigKey.SessionTimeout]), SessionID, false);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Assigned(FWebSession) then
|
|
|
|
|
raise EMVCException.Create('Invalid SessionID');
|
|
|
|
|
FWebSession.MarkAsUsed;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
TMVCEngine.SendSessionCookie(Self, SessionID);
|
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;
|
|
|
|
|
|
|
|
|
|
constructor TMVCController.Create;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited Create;
|
|
|
|
|
FContentCharset := TMVCConstants.DEFAULT_CONTENT_CHARSET;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
destructor TMVCController.Destroy;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FreeAndNil(FResponseStream);
|
|
|
|
|
FreeAndNil(FViewDataSets);
|
|
|
|
|
FreeAndNil(FViewModel);
|
|
|
|
|
inherited;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.EnqueueMessageOnTopicOrQueue(const IsQueue: Boolean;
|
|
|
|
|
const ATopic: string; AJSONObject: TJSONObject; aOwnsInstance: Boolean);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Stomp: IStompClient;
|
|
|
|
|
H: IStompHeaders;
|
|
|
|
|
msg: TJSONObject;
|
|
|
|
|
begin
|
|
|
|
|
msg := TJSONObject.Create;
|
|
|
|
|
try
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if aOwnsInstance then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
msg.AddPair('message', AJSONObject)
|
|
|
|
|
else
|
|
|
|
|
msg.AddPair('message', AJSONObject.Clone as TJSONObject);
|
|
|
|
|
|
|
|
|
|
if IsQueue then
|
|
|
|
|
msg.AddPair('_queue', ATopic)
|
|
|
|
|
else
|
|
|
|
|
msg.AddPair('_topic', ATopic);
|
|
|
|
|
|
|
|
|
|
msg.AddPair('_username', GetClientID).AddPair('_timestamp',
|
2016-02-28 19:06:05 +01:00
|
|
|
|
FormatDateTime('YYYY-MM-DD HH:NN:SS', Now));
|
2015-12-02 04:14:15 +01:00
|
|
|
|
|
|
|
|
|
Stomp := GetNewStompClient(GetClientID);
|
|
|
|
|
H := StompUtils.NewHeaders.Add(TStompHeaders.NewPersistentHeader(true));
|
2015-04-10 11:45:45 +02:00
|
|
|
|
{$IF CompilerVersion >= 28}
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Stomp.Send(ATopic, msg.ToJSON);
|
2015-04-10 11:45:45 +02:00
|
|
|
|
{$ELSE}
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Stomp.Send(ATopic, msg.ToString);
|
2015-04-10 11:45:45 +02:00
|
|
|
|
{$ENDIF}
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TThread.Sleep(100);
|
|
|
|
|
// single user cannot enqueue more than 10 message in noe second...
|
|
|
|
|
// it is noot too much elegant, but it works as DoS protection
|
|
|
|
|
finally
|
|
|
|
|
msg.Free;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-04-01 17:01:23 +02:00
|
|
|
|
function TMVCController.GetClientID: string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := Session[CLIENTID_KEY];
|
|
|
|
|
if Result.IsEmpty then
|
|
|
|
|
// 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 ?');
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2014-06-27 15:30:39 +02:00
|
|
|
|
function TMVCController.GetContentCharset: string;
|
2013-11-05 14:57:50 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01: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
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FContext.Response.ContentType;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2013-11-10 01:04:17 +01:00
|
|
|
|
function TMVCController.GetCurrentWebModule: TWebModule;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := GetMVCEngine.Owner as TWebModule;
|
2013-11-10 01:04:17 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
function TMVCController.GetNewStompClient(ClientID: string): IStompClient;
|
|
|
|
|
begin
|
2016-08-09 13:08:17 +02:00
|
|
|
|
raise EMVCException.Create('Not Implemented');
|
2016-09-05 15:34:17 +02:00
|
|
|
|
// Result := StompUtils.NewStomp(Config[TMVCConfigKey.StompServer],
|
|
|
|
|
// StrToInt(Config[TMVCConfigKey.StompServerPort]), GetClientID,
|
|
|
|
|
// Config[TMVCConfigKey.StompUsername], Config[TMVCConfigKey.StompPassword]);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-12-05 15:50:00 +01:00
|
|
|
|
function TMVCController.SessionAs<T>: T;
|
|
|
|
|
begin
|
|
|
|
|
Result := Session as T;
|
|
|
|
|
end;
|
|
|
|
|
|
2016-02-27 09:58:54 +01:00
|
|
|
|
function TMVCController.GetRenderedView(const ViewNames
|
2016-08-09 13:08:17 +02:00
|
|
|
|
: TArray<string>): string;
|
2016-02-27 09:58:54 +01:00
|
|
|
|
var
|
|
|
|
|
View: TMVCMustacheView;
|
2016-08-09 13:08:17 +02:00
|
|
|
|
LViewName: string;
|
2016-02-27 09:58:54 +01:00
|
|
|
|
LSBuilder: TStringBuilder;
|
|
|
|
|
begin
|
|
|
|
|
LSBuilder := TStringBuilder.Create;
|
|
|
|
|
try
|
|
|
|
|
try
|
|
|
|
|
for LViewName in ViewNames do
|
|
|
|
|
begin
|
|
|
|
|
|
|
|
|
|
View := TMVCMustacheView.Create(LViewName, GetMVCEngine, FContext,
|
|
|
|
|
FViewModel, FViewDataSets, ContentType);
|
|
|
|
|
try
|
|
|
|
|
View.SetMVCConfig(GetMVCConfig);
|
|
|
|
|
View.Execute;
|
|
|
|
|
LSBuilder.Append(View.GetOutput);
|
|
|
|
|
finally
|
|
|
|
|
View.Free;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
Result := LSBuilder.ToString;
|
|
|
|
|
except
|
|
|
|
|
on E: Exception do
|
|
|
|
|
begin
|
|
|
|
|
ContentType := 'text/plain';
|
|
|
|
|
Render(E);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
finally
|
|
|
|
|
LSBuilder.Free;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
function TMVCController.GetWebSession: TWebSession;
|
|
|
|
|
begin
|
2016-04-03 22:35:27 +02:00
|
|
|
|
Result := FContext.Session;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-09-25 16:17:37 +02:00
|
|
|
|
function TMVCController.LoadView(const ViewNames: TArray<string>): string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-02-27 09:58:54 +01:00
|
|
|
|
try
|
2016-09-25 16:17:37 +02:00
|
|
|
|
Result := GetRenderedView(ViewNames);
|
|
|
|
|
ResponseStream.Append(Result);
|
2016-02-27 09:58:54 +01:00
|
|
|
|
except
|
|
|
|
|
on E: Exception do
|
|
|
|
|
begin
|
2016-10-02 17:44:20 +02:00
|
|
|
|
LogException(E);
|
2016-02-27 09:58:54 +01:00
|
|
|
|
ContentType := 'text/plain';
|
|
|
|
|
Render(E);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-10-02 17:44:20 +02:00
|
|
|
|
procedure TMVCController.LoadViewFragment(const ViewFragment: string);
|
|
|
|
|
begin
|
|
|
|
|
ResponseStream.Append(ViewFragment);
|
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
procedure TMVCController.MVCControllerAfterCreate;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCController.MVCControllerBeforeDestroy;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCController.OnAfterAction(Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const aActionName: string);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
// do nothing
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCController.OnBeforeAction(Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
const aActionName: string; var Handled: Boolean);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Handled := false;
|
|
|
|
|
if ContentType.IsEmpty then
|
|
|
|
|
ContentType := GetMVCConfig[TMVCConfigKey.DefaultContentType];
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCController.PushDataSetToView(const AModelName: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
aDataSet: TDataSet);
|
2015-10-18 16:35:50 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LJArr: TJSONArray;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LJArr := TJSONArray.Create;
|
|
|
|
|
try
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Mapper.DataSetToJSONArray(aDataSet, LJArr, true);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
except
|
|
|
|
|
LJArr.Free;
|
|
|
|
|
raise;
|
|
|
|
|
end;
|
|
|
|
|
PushJSONToView(AModelName, LJArr);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCController.PushJSONToView(const AModelName: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
AModel: TJSONValue);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Assigned(FViewModel) then
|
|
|
|
|
FViewModel := TMVCDataObjects.Create;
|
|
|
|
|
FViewModel.Add(AModelName, AModel);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCController.PushObjectToView(const AModelName: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
AModel: TObject);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
PushJSONToView(AModelName, Mapper.ObjectToJSONObject(AModel));
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure InternalRenderText(const AContent: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ContentType, ContentEncoding: string; Context: TWebContext);
|
2014-04-01 00:02:31 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
OutEncoding: TEncoding;
|
2016-12-13 18:16:09 +01:00
|
|
|
|
lContentType: String;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-12-13 18:16:09 +01:00
|
|
|
|
lContentType := ContentType + '; charset=' + ContentEncoding;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
OutEncoding := TEncoding.GetEncoding(ContentEncoding);
|
|
|
|
|
try
|
|
|
|
|
// Context.Response.RawWebResponse.ContentStream := TStringStream.Create(UTF8Encode(AContent));
|
|
|
|
|
if SameText('UTF-8', ContentEncoding) then
|
|
|
|
|
begin
|
2016-12-13 18:16:09 +01:00
|
|
|
|
Context.Response.SetContentStream(
|
|
|
|
|
// TStringStream.Create(UTF8Encode(AContent), TEncoding.UTF8),
|
|
|
|
|
TStringStream.Create( { UTF8Encode( } AContent { ) } , TEncoding.UTF8),
|
|
|
|
|
lContentType);
|
|
|
|
|
// Context.Response.RawWebResponse.Content := '';
|
|
|
|
|
// Context.Response.RawWebResponse.ContentStream :=
|
|
|
|
|
// TStringStream.Create(UTF8Encode(AContent));
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2016-12-13 18:16:09 +01:00
|
|
|
|
Context.Response.SetContentStream(
|
|
|
|
|
TBytesStream.Create(
|
|
|
|
|
TEncoding.Convert(TEncoding.Default, OutEncoding, TEncoding.Default.GetBytes(AContent))),
|
|
|
|
|
lContentType
|
|
|
|
|
);
|
|
|
|
|
// Context.Response.RawWebResponse.Content :=
|
|
|
|
|
// OutEncoding.GetString(TEncoding.Convert(TEncoding.UTF8, OutEncoding,
|
|
|
|
|
// TEncoding.Default.GetBytes(AContent)));
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
finally
|
|
|
|
|
OutEncoding.Free;
|
|
|
|
|
end;
|
|
|
|
|
// 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;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure InternalRender(aJSONValue: TJSONValue;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ContentType, ContentEncoding: string; Context: TWebContext;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
aInstanceOwner: Boolean);
|
2013-12-04 13:06:18 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
OutEncoding: TEncoding;
|
2016-12-13 18:16:09 +01:00
|
|
|
|
lContentType, lJString: string;
|
2013-12-04 13:06:18 +01:00
|
|
|
|
begin
|
2015-01-14 14:13:48 +01:00
|
|
|
|
{$IF CompilerVersion <= 27}
|
2016-12-13 18:16:09 +01:00
|
|
|
|
lJString := aJSONValue.ToString; // requires the patch
|
2015-01-14 14:13:48 +01:00
|
|
|
|
{$ELSE}
|
2016-12-13 18:16:09 +01:00
|
|
|
|
lJString := aJSONValue.ToJSON; // since XE7 is available ToJSON
|
2015-01-14 14:13:48 +01:00
|
|
|
|
{$ENDIF}
|
2016-03-01 22:50:32 +01:00
|
|
|
|
// first set the ContentType; because of this bug:
|
|
|
|
|
// http://qc.embarcadero.com/wc/qcmain.aspx?d=67350
|
|
|
|
|
Context.Response.RawWebResponse.ContentType := ContentType + '; charset=' +
|
|
|
|
|
ContentEncoding;
|
2016-12-13 18:16:09 +01:00
|
|
|
|
lContentType := ContentType + '; charset=' +
|
|
|
|
|
ContentEncoding;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
OutEncoding := TEncoding.GetEncoding(ContentEncoding);
|
|
|
|
|
try
|
2016-12-13 18:16:09 +01:00
|
|
|
|
Context.Response.SetContentStream(
|
|
|
|
|
TBytesStream.Create(
|
|
|
|
|
TEncoding.Convert(TEncoding.Default, OutEncoding,
|
|
|
|
|
TEncoding.Default.GetBytes(lJString))
|
|
|
|
|
), lContentType);
|
|
|
|
|
|
|
|
|
|
// Context.Response.SetContent(
|
|
|
|
|
// OutEncoding.GetString(
|
|
|
|
|
// TEncoding.Convert(TEncoding.Default, OutEncoding,
|
|
|
|
|
// TEncoding.Default.GetBytes(lJString))
|
|
|
|
|
// ));
|
|
|
|
|
|
|
|
|
|
// Context.Response.RawWebResponse.Content :=
|
|
|
|
|
// OutEncoding.GetString(TEncoding.Convert(TEncoding.Default, OutEncoding,
|
|
|
|
|
// TEncoding.Default.GetBytes(JString)));
|
2015-12-02 04:14:15 +01:00
|
|
|
|
finally
|
|
|
|
|
OutEncoding.Free;
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if aInstanceOwner then
|
|
|
|
|
FreeAndNil(aJSONValue)
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure InternalRender(const Content: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
ContentType, ContentEncoding: string; Context: TWebContext);
|
|
|
|
|
begin
|
|
|
|
|
if ContentType = TMVCMimeType.APPLICATION_JSON then
|
|
|
|
|
begin
|
|
|
|
|
InternalRender(TJSONString.Create(Content), ContentType, ContentEncoding,
|
|
|
|
|
Context, true);
|
|
|
|
|
end
|
|
|
|
|
else if ContentType = TMVCMimeType.TEXT_XML then
|
|
|
|
|
begin
|
|
|
|
|
raise EMVCException.Create('Format still not supported - ' + ContentType);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
if ContentType.IsEmpty then
|
|
|
|
|
InternalRenderText(Content, 'text/plain', ContentEncoding, Context)
|
|
|
|
|
else
|
|
|
|
|
InternalRenderText(Content, ContentType, ContentEncoding, Context);
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-09-25 16:17:37 +02:00
|
|
|
|
procedure TMVCController.RenderResponseStream;
|
|
|
|
|
begin
|
|
|
|
|
InternalRenderText(ResponseStream.ToString, ContentType, ContentCharset, Context);
|
|
|
|
|
end;
|
|
|
|
|
|
2014-04-16 22:52:25 +02:00
|
|
|
|
procedure TMVCController.Render(const Content: string);
|
2013-12-04 13:06:18 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
InternalRender(Content, ContentType, ContentCharset, Context);
|
2013-12-04 13:06:18 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.Render(aObject: TObject; aInstanceOwner: Boolean;
|
|
|
|
|
aSerializationType: TDMVCSerializationType);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
JSON: TJSONObject;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if aSerializationType = TDMVCSerializationType.Properties then
|
|
|
|
|
JSON := Mapper.ObjectToJSONObject(aObject)
|
2015-12-02 04:14:15 +01:00
|
|
|
|
else
|
2016-06-22 17:49:16 +02:00
|
|
|
|
JSON := Mapper.ObjectToJSONObjectFields(aObject, []);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Render(JSON, true);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if aInstanceOwner then
|
|
|
|
|
FreeAndNil(aObject);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCController.SendFile(AFileName: string);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TMVCStaticContents.SendFile(AFileName, ContentType, Context);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-04-03 22:35:27 +02:00
|
|
|
|
function TWebContext.SendSessionCookie(AContext: TWebContext): string;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2016-04-03 22:35:27 +02:00
|
|
|
|
Result := TMVCEngine.SendSessionCookie(Self);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-10-10 16:09:43 +02:00
|
|
|
|
procedure TMVCController.SendStream(AStream: TStream; AOwnStream: Boolean;
|
|
|
|
|
ARewindStream: Boolean);
|
2016-11-18 18:25:09 +01:00
|
|
|
|
var
|
|
|
|
|
lStream: TStream;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2016-10-10 16:09:43 +02:00
|
|
|
|
if ARewindStream then
|
|
|
|
|
AStream.Position := 0;
|
2016-11-18 18:25:09 +01:00
|
|
|
|
|
|
|
|
|
if not AOwnStream then
|
|
|
|
|
begin
|
|
|
|
|
lStream := TMemoryStream.Create;
|
|
|
|
|
lStream.CopyFrom(AStream, 0);
|
2016-12-13 18:16:09 +01:00
|
|
|
|
lStream.Position := 0;
|
2016-11-18 18:25:09 +01:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
lStream := AStream;
|
|
|
|
|
end;
|
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FContext.Response.FWebResponse.Content := '';
|
|
|
|
|
FContext.Response.FWebResponse.ContentType := ContentType;
|
2016-11-18 18:25:09 +01:00
|
|
|
|
FContext.Response.FWebResponse.ContentStream := lStream;
|
2016-12-05 15:50:00 +01:00
|
|
|
|
FContext.Response.FWebResponse.FreeContentStream := true;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-08-09 13:08:17 +02:00
|
|
|
|
function TWebContext.SessionID: string;
|
2016-06-28 13:42:14 +02:00
|
|
|
|
begin
|
|
|
|
|
if Assigned(FWebSession) then
|
|
|
|
|
Exit(FWebSession.SessionID);
|
|
|
|
|
Result := FRequest.Cookie(TMVCConstants.SESSION_TOKEN_NAME);
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TWebContext.SessionMustBeClose: Boolean;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
begin
|
|
|
|
|
Result := FSessionMustBeClose;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TWebContext.SessionStart;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LSessionID: string;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Assigned(FWebSession) then
|
|
|
|
|
begin
|
2016-04-03 22:35:27 +02:00
|
|
|
|
LSessionID := TMVCEngine.SendSessionCookie(Self);
|
2016-12-05 15:50:00 +01:00
|
|
|
|
FWebSession := AddSessionToTheSessionList(
|
|
|
|
|
Config[TMVCConfigKey.SessionType],
|
|
|
|
|
LSessionID,
|
2015-12-02 04:14:15 +01:00
|
|
|
|
StrToInt64(Config[TMVCConfigKey.SessionTimeout]));
|
|
|
|
|
FIsSessionStarted := true;
|
|
|
|
|
FSessionMustBeClose := false;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-28 13:42:14 +02:00
|
|
|
|
function TWebContext.SessionStarted: Boolean;
|
|
|
|
|
var
|
|
|
|
|
LSessionID: string;
|
|
|
|
|
begin
|
|
|
|
|
LSessionID := SessionID;
|
|
|
|
|
if LSessionID.IsEmpty then
|
|
|
|
|
Exit(false);
|
|
|
|
|
TMonitor.Enter(SessionList);
|
|
|
|
|
try
|
|
|
|
|
Result := SessionList.ContainsKey(LSessionID);
|
|
|
|
|
finally
|
|
|
|
|
TMonitor.Exit(SessionList);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TWebContext.SessionStop(ARaiseExceptionIfExpired: Boolean);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Cookie: TCookie;
|
2016-04-03 22:35:27 +02:00
|
|
|
|
LSessionID: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
// Set-Cookie: token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT
|
2016-04-03 22:35:27 +02:00
|
|
|
|
FResponse.Cookies.Clear; // daniele ... remove all previous cookies
|
|
|
|
|
Cookie := FResponse.Cookies.Add;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Cookie.Name := TMVCConstants.SESSION_TOKEN_NAME;
|
|
|
|
|
|
|
|
|
|
// rubbish... invalid the cookie value
|
|
|
|
|
Cookie.Value := GUIDToString(TGUID.NewGuid) + 'invalid' +
|
|
|
|
|
GUIDToString(TGUID.NewGuid);
|
|
|
|
|
Cookie.Expires := EncodeDate(1970, 1, 1);
|
|
|
|
|
Cookie.Path := '/';
|
|
|
|
|
|
|
|
|
|
TMonitor.Enter(SessionList);
|
|
|
|
|
try
|
2016-04-03 22:35:27 +02:00
|
|
|
|
LSessionID := TMVCEngine.ExtractSessionIDFromWebRequest
|
|
|
|
|
(FRequest.RawWebRequest);
|
|
|
|
|
// if not Assigned(FWebSession) then
|
|
|
|
|
// FWebSession := TMVCEngine.GetCurrentSession
|
|
|
|
|
// (StrToInt64(FConfig[TMVCConfigKey.SessionTimeout]), '',
|
|
|
|
|
// ARaiseExceptionIfExpired);
|
|
|
|
|
// if Assigned(FWebSession) then
|
|
|
|
|
SessionList.Remove(LSessionID);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
finally
|
|
|
|
|
TMonitor.Exit(SessionList);
|
|
|
|
|
end;
|
|
|
|
|
FIsSessionStarted := false;
|
|
|
|
|
FSessionMustBeClose := true;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-06-27 15:30:39 +02:00
|
|
|
|
procedure TMVCController.SetContentCharset(const Value: string);
|
2013-11-05 14:57:50 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01: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
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FContext.Response.ContentType := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-04-16 22:52:25 +02:00
|
|
|
|
procedure TMVCController.SetContext(const Value: TWebContext);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if FContext = nil then
|
|
|
|
|
FContext := Value
|
|
|
|
|
else
|
|
|
|
|
raise EMVCException.Create('Context already set');
|
2013-10-30 00:48:23 +01:00
|
|
|
|
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
|
2016-04-03 22:35:27 +02:00
|
|
|
|
raise Exception.Create('Qualcuno mi usa...');
|
|
|
|
|
// if Assigned(FContext.FWebSession) then
|
|
|
|
|
// raise EMVCException.Create('Web Session already set for controller ' +
|
|
|
|
|
// ClassName);
|
|
|
|
|
// FContext.FWebSession := Value;
|
|
|
|
|
// FIsSessionStarted := Assigned(FContext.FWebSession);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
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
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited Create;
|
|
|
|
|
FPath := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-23 12:11:01 +02:00
|
|
|
|
function TMVCWebRequest.QueryStringParams: TStrings;
|
2016-06-23 11:42:16 +02:00
|
|
|
|
begin
|
|
|
|
|
Result := FWebRequest.QueryFields;
|
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
function TMVCWebRequest.QueryStringParam(Name: string): string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebRequest.QueryFields.Values[name];
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-08-09 13:08:17 +02:00
|
|
|
|
procedure TMVCWebRequest.EnsureQueryParamExists(const Name: string);
|
2016-05-11 10:39:20 +02:00
|
|
|
|
begin
|
2016-08-09 13:08:17 +02:00
|
|
|
|
if GetParamAll(name).IsEmpty then
|
|
|
|
|
raise EMVCException.CreateFmt('Parameter "%s" required', [name]);
|
2016-05-11 10:39:20 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TMVCWebRequest.QueryStringParamExists(Name: string): Boolean;
|
2013-11-08 23:10:25 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := not QueryStringParam(name).IsEmpty;
|
2013-11-08 23:10:25 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TMVCWebRequest.GetClientPreferHTML: Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := ClientPrefer(TMVCMimeType.TEXT_HTML);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.GetFiles: TAbstractWebRequestFiles;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebRequest.Files;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
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
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if Assigned(FWebRequest) then
|
|
|
|
|
Result := FWebRequest.GetFieldByName(name)
|
|
|
|
|
else
|
|
|
|
|
Result := '';
|
2014-04-10 13:56:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2015-04-01 17:01:23 +02:00
|
|
|
|
// function TMVCWebRequest.GetHeaderValue(const Name: string): string;
|
|
|
|
|
// var
|
|
|
|
|
// S: string;
|
|
|
|
|
// begin
|
|
|
|
|
// S := GetHeader(name);
|
|
|
|
|
// if S.IsEmpty then
|
|
|
|
|
// Result := ''
|
|
|
|
|
// else
|
|
|
|
|
// Result := S.Split([':'])[1].trim;
|
|
|
|
|
// end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
|
|
|
|
// function TMVCWebRequest.GetHeaderAll(const HeaderName: string): string;
|
|
|
|
|
// begin
|
|
|
|
|
// Result := Self.FWebRequest.GetFieldByName(HeaderName);
|
|
|
|
|
// end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.GetHTTPMethod: TMVCHTTPMethodType;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := TMVCRouter.StringMethodToHTTPMetod(FWebRequest.Method);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.GetHTTPMethodAsString: string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebRequest.Method;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TMVCWebRequest.GetIsAjax: Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := LowerCase(FWebRequest.GetFieldByName('X-Requested-With'))
|
|
|
|
|
= 'xmlhttprequest';
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-08-09 13:08:17 +02:00
|
|
|
|
function TMVCWebRequest.GetSegmentParam(const ParamName: string;
|
|
|
|
|
out Value: string): Boolean;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
begin
|
|
|
|
|
if (not Assigned(FParamsTable)) then
|
|
|
|
|
Exit(false);
|
|
|
|
|
Result := FParamsTable.TryGetValue(ParamName, Value);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCWebRequest.GetSegmentParamsCount: Integer;
|
|
|
|
|
begin
|
|
|
|
|
if Assigned(FParamsTable) then
|
|
|
|
|
Result := FParamsTable.Count
|
|
|
|
|
else
|
|
|
|
|
Result := 0;
|
|
|
|
|
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
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if (not Assigned(FParamsTable)) or
|
|
|
|
|
(not FParamsTable.TryGetValue(ParamName, Result)) then
|
|
|
|
|
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;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-09-27 14:33:51 +02:00
|
|
|
|
function TMVCWebRequest.GetParamAllAsInt64(const ParamName: string): Int64;
|
|
|
|
|
begin
|
|
|
|
|
Result := StrToInt64(GetParamAll(ParamName));
|
|
|
|
|
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
|
2016-06-22 17:49:16 +02: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
|
2016-09-16 23:54:54 +02:00
|
|
|
|
I: Integer;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Names: TList<string>;
|
|
|
|
|
n: string;
|
2013-11-12 01:23:50 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if Length(FParamNames) > 0 then
|
|
|
|
|
Exit(FParamNames);
|
2013-11-12 01:23:50 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Names := TList<string>.Create;
|
|
|
|
|
try
|
|
|
|
|
if Assigned(FParamsTable) and (Length(FParamsTable.Keys.ToArray) > 0) then
|
|
|
|
|
for n in FParamsTable.Keys.ToArray do
|
|
|
|
|
Names.Add(n);
|
2013-11-12 01:23:50 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if FWebRequest.QueryFields.Count > 0 then
|
2016-09-16 23:54:54 +02:00
|
|
|
|
for I := 0 to FWebRequest.QueryFields.Count - 1 do
|
|
|
|
|
Names.Add(FWebRequest.QueryFields.Names[I]);
|
2013-11-12 01:23:50 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if FWebRequest.ContentFields.Count > 0 then
|
2016-09-16 23:54:54 +02:00
|
|
|
|
for I := 0 to FWebRequest.ContentFields.Count - 1 do
|
|
|
|
|
Names.Add(FWebRequest.ContentFields.Names[I]);
|
2013-11-12 01:23:50 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if FWebRequest.CookieFields.Count > 0 then
|
2016-09-16 23:54:54 +02:00
|
|
|
|
for I := 0 to FWebRequest.CookieFields.Count - 1 do
|
|
|
|
|
Names.Add(FWebRequest.CookieFields.Names[I]);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := Names.ToArray;
|
|
|
|
|
finally
|
|
|
|
|
Names.Free;
|
|
|
|
|
end;
|
2013-11-12 01:23:50 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
function TMVCWebRequest.GetPathInfo: string;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebRequest.PathInfo;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCWebRequest.SetParamsTable(AParamsTable: TMVCRequestParamsTable);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FParamsTable := AParamsTable;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TMVCWebRequest.ThereIsRequestBody: Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FWebRequest.Content <> '';
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ MVCHTTPMethodAttribute }
|
|
|
|
|
|
|
|
|
|
constructor MVCHTTPMethodAttribute.Create(AMVCHTTPMethods: TMVCHTTPMethods);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited Create;
|
|
|
|
|
FMVCHTTPMethods := AMVCHTTPMethods;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function MVCHTTPMethodAttribute.GetMVCHTTPMethodsAsString: string;
|
|
|
|
|
var
|
2016-09-16 23:54:54 +02:00
|
|
|
|
I: TMVCHTTPMethodType;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := '';
|
2016-09-16 23:54:54 +02:00
|
|
|
|
for I := low(TMVCHTTPMethodType) to high(TMVCHTTPMethodType) do
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-09-16 23:54:54 +02:00
|
|
|
|
if I in FMVCHTTPMethods then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
Result := Result + ',' + GetEnumName
|
2016-09-16 23:54:54 +02:00
|
|
|
|
(TypeInfo(TMVCHTTPMethodType), Ord(I));
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Result.IsEmpty then
|
|
|
|
|
Result := Result.Remove(0, 1)
|
|
|
|
|
else
|
|
|
|
|
Result := 'any';
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ TMVCStaticContents }
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
procedure TMVCStaticContents.SendFile(AFileName, AMimeType: string;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Context: TWebContext);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LFileDate: TDateTime;
|
|
|
|
|
LReqDate: TDateTime;
|
|
|
|
|
S: TFileStream;
|
|
|
|
|
begin
|
|
|
|
|
LFileDate := IndyFileAge(AFileName);
|
|
|
|
|
if (LFileDate = 0.0) and (not FileExists(AFileName)) then
|
|
|
|
|
begin
|
|
|
|
|
Context.Response.StatusCode := 404;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
LReqDate := GMTToLocalDateTime(Context.Request.Headers
|
|
|
|
|
['If-Modified-Since']);
|
|
|
|
|
if (LReqDate <> 0) and (abs(LReqDate - LFileDate) < 2 * (1 / (24 * 60 * 60)))
|
|
|
|
|
then
|
|
|
|
|
begin
|
|
|
|
|
Context.Response.ContentType := AMimeType;
|
|
|
|
|
Context.Response.StatusCode := 304;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
S := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
|
2016-03-23 15:24:55 +01:00
|
|
|
|
// Content-Length is set in (%DELPHI%)\source\internet\Web.Win.IsapiHTTP.pas
|
|
|
|
|
// procedure TISAPIResponse.SendResponse;
|
|
|
|
|
// if set twice it could be a problem under IIS (ISAPI)
|
|
|
|
|
// the header is available 1x but the value are doubled
|
|
|
|
|
// sometimes some images are not shown
|
|
|
|
|
// How to unittest this behavior?
|
|
|
|
|
// Context.Response.SetCustomHeader('Content-Length', IntToStr(S.Size));
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Context.Response.SetCustomHeader('Last-Modified',
|
|
|
|
|
LocalDateTimeToHttpStr(LFileDate));
|
|
|
|
|
Context.Response.SetContentStream(S, AMimeType);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
function TMVCStaticContents.IsScriptableFile(StaticFileName: string;
|
|
|
|
|
Config: TMVCConfig): Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := TPath.GetExtension(StaticFileName).ToLower = '.' +
|
|
|
|
|
Config[TMVCConfigKey.DefaultViewFileExtension].ToLower;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
function TMVCStaticContents.IsStaticFile(AViewPath, AWebRequestPath
|
|
|
|
|
: string; out ARealFileName: string): Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FileName: string;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if TDirectory.Exists(AViewPath) then // absolute path
|
|
|
|
|
FileName := AViewPath + AWebRequestPath.Replace('/',
|
|
|
|
|
TPath.DirectorySeparatorChar)
|
|
|
|
|
else
|
|
|
|
|
FileName := GetApplicationFileNamePath + AViewPath +
|
|
|
|
|
// relative path
|
|
|
|
|
AWebRequestPath.Replace('/', TPath.DirectorySeparatorChar);
|
|
|
|
|
Result := TFile.Exists(FileName);
|
|
|
|
|
ARealFileName := FileName;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-04-16 22:52:25 +02:00
|
|
|
|
procedure TMVCBase.SetApplicationSession(const Value: TWebApplicationSession);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if Assigned(FApplicationSession) then
|
|
|
|
|
raise EMVCException.Create('Application Session already set');
|
|
|
|
|
FApplicationSession := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-04-16 22:52:25 +02:00
|
|
|
|
procedure TMVCBase.SetMVCConfig(const Value: TMVCConfig);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FMVCConfig := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-04-16 22:52:25 +02:00
|
|
|
|
procedure TMVCBase.SetMVCEngine(const Value: TMVCEngine);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FMVCEngine := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
function TMVCBase.GetApplicationFileName: string;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
fname: PChar;
|
|
|
|
|
Size: Integer;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := '';
|
|
|
|
|
fname := GetMemory(2048);
|
|
|
|
|
try
|
|
|
|
|
Size := GetModuleFileName(0, fname, 2048);
|
|
|
|
|
if Size > 0 then
|
|
|
|
|
Result := fname;
|
|
|
|
|
finally
|
|
|
|
|
FreeMem(fname, 2048);
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
class
|
|
|
|
|
function TMVCBase.GetApplicationFileNamePath: string;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := IncludeTrailingPathDelimiter
|
|
|
|
|
(ExtractFilePath(GetApplicationFileName));
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCBase.GetMVCConfig: TMVCConfig;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Assigned(FMVCConfig) then
|
|
|
|
|
EMVCConfigException.Create('MVCConfig not assigned to this ' + ClassName +
|
|
|
|
|
' instances');
|
|
|
|
|
Result := FMVCConfig;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCBase.GetMVCEngine: TMVCEngine;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := FMVCEngine;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ TMVCISAPIWebRequest }
|
|
|
|
|
|
|
|
|
|
constructor TMVCISAPIWebRequest.Create(AWebRequest: TWebRequest);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited;
|
|
|
|
|
FWebRequest := AWebRequest as TISAPIRequest;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-05-14 15:55:41 +02:00
|
|
|
|
{ TMVCApacheWebRequest }
|
2014-09-29 17:42:34 +02:00
|
|
|
|
{$IF CompilerVersion >= 27}
|
2014-05-14 15:55:41 +02:00
|
|
|
|
|
2016-05-11 10:39:20 +02:00
|
|
|
|
|
2014-05-14 15:55:41 +02:00
|
|
|
|
constructor TMVCApacheWebRequest.Create(AWebRequest: TWebRequest);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited;
|
|
|
|
|
FWebRequest := AWebRequest as TApacheRequest;
|
2014-05-14 15:55:41 +02:00
|
|
|
|
end;
|
|
|
|
|
{$ENDIF}
|
2013-10-30 00:48:23 +01:00
|
|
|
|
{ TMVCINDYWebRequest }
|
|
|
|
|
|
|
|
|
|
constructor TMVCINDYWebRequest.Create(AWebRequest: TWebRequest);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited;
|
|
|
|
|
FWebRequest := AWebRequest; // as TIdHTTPAppRequest;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
{ TWebSession }
|
|
|
|
|
|
2014-05-05 18:52:49 +02:00
|
|
|
|
procedure TMVCController.RaiseSessionExpired;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
raise EMVCSessionExpiredException.Create('Session expired');
|
2014-05-05 18:52:49 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
function TMVCController.ReceiveMessageFromTopic(const ATopic: string;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
ATimeout: Int64; var JSONObject: TJSONObject): Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Stomp: IStompClient;
|
|
|
|
|
frame: IStompFrame;
|
|
|
|
|
o: TJSONValue;
|
|
|
|
|
begin
|
|
|
|
|
Result := false;
|
|
|
|
|
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
|
|
|
|
|
if not(o is TJSONObject) then
|
|
|
|
|
begin
|
|
|
|
|
FreeAndNil(o);
|
|
|
|
|
raise EMVCException.Create
|
|
|
|
|
('Message is a JSONValue but not a JSONObject')
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
JSONObject := TJSONObject(o);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCController.Redirect(const URL: string);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FContext.Response.FWebResponse.SendRedirect(URL);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
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
|
2015-12-02 04:14:15 +01:00
|
|
|
|
j: TJSONObject;
|
|
|
|
|
S: string;
|
|
|
|
|
jarr: TJSONArray;
|
|
|
|
|
begin
|
|
|
|
|
if E is EMVCException then
|
|
|
|
|
ResponseStatusCode(EMVCException(E).HTTPErrorCode,
|
|
|
|
|
E.Message + ' [' + E.ClassName + ']')
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
if Context.Response.StatusCode = 200 then
|
|
|
|
|
ResponseStatusCode(500, E.Message + ' [' + E.ClassName + ']');
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
if (not Context.Request.IsAjax) and (Context.Request.ClientPreferHTML) then
|
|
|
|
|
begin
|
|
|
|
|
ContentType := TMVCMimeType.TEXT_HTML;
|
|
|
|
|
ContentCharset := TMVCConstants.DEFAULT_CONTENT_CHARSET;
|
|
|
|
|
ResponseStream.Clear;
|
|
|
|
|
|
|
|
|
|
ResponseStream.Append
|
|
|
|
|
('<html><head><style>pre { color: #000000; background-color: #d0d0d0; }</style></head><body>')
|
2016-02-28 19:06:05 +01:00
|
|
|
|
.Append('<h1>' + Config[TMVCConfigKey.ServerName] + ': Error Raised</h1>')
|
2015-12-02 04:14:15 +01:00
|
|
|
|
.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>');
|
|
|
|
|
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
|
|
|
|
|
begin
|
|
|
|
|
j := TJSONObject.Create;
|
|
|
|
|
j.AddPair('status', 'error');
|
|
|
|
|
j.AddPair('classname', E.ClassName);
|
|
|
|
|
j.AddPair('message', E.Message);
|
|
|
|
|
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;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.Render(const aErrorCode: UInt16;
|
|
|
|
|
const aErrorMessage: string; const AErrorClassName: string = '');
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
j: TJSONObject;
|
|
|
|
|
status: string;
|
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
ResponseStatusCode(aErrorCode, aErrorMessage);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if Context.Request.IsAjax or (ContentType = 'application/json') then
|
|
|
|
|
begin
|
|
|
|
|
status := 'error';
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if (aErrorCode div 100) = 2 then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
status := 'ok';
|
|
|
|
|
j := TJSONObject.Create;
|
|
|
|
|
j.AddPair('status', status);
|
|
|
|
|
if AErrorClassName = '' then
|
|
|
|
|
j.AddPair('classname', TJSONNull.Create)
|
|
|
|
|
else
|
|
|
|
|
j.AddPair('classname', AErrorClassName);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
j.AddPair('message', aErrorMessage);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Render(j);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2016-12-05 14:03:58 +01:00
|
|
|
|
Render(Format('Error: [%d] %s', [aErrorCode, aErrorMessage]));
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.Render(aDataSet: TDataSet; aInstanceOwner: Boolean;
|
|
|
|
|
aOnlySingleRecord: Boolean; aJSONObjectActionProc: TJSONObjectActionProc);
|
2013-11-10 01:04:17 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
arr: TJSONArray;
|
|
|
|
|
JObj: TJSONObject;
|
|
|
|
|
begin
|
|
|
|
|
if ContentType = TMVCMimeType.APPLICATION_JSON then
|
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if not aOnlySingleRecord then
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
aDataSet.First;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
arr := TJSONArray.Create;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Mapper.DataSetToJSONArray(aDataSet, arr, aInstanceOwner,
|
|
|
|
|
aJSONObjectActionProc);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Render(arr);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
JObj := TJSONObject.Create;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Mapper.DataSetToJSONObject(aDataSet, JObj, aInstanceOwner,
|
|
|
|
|
aJSONObjectActionProc);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Render(JObj);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
raise Exception.Create('ContentType not supported for this render [' +
|
|
|
|
|
ContentType + ']');
|
|
|
|
|
// if ContentType = TMVCMimeType.TEXT_XML then
|
|
|
|
|
// begin
|
|
|
|
|
// Mapper.DataSetToXML(ADataSet, S, AInstanceOwner);
|
|
|
|
|
// Render(S);
|
|
|
|
|
// end;
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCController.Render(const AStream: TStream;
|
2016-06-22 17:49:16 +02:00
|
|
|
|
aInstanceOwner: Boolean);
|
2015-10-18 16:35:50 +02:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
SendStream(AStream, aInstanceOwner);
|
2015-10-18 16:35:50 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-09-29 18:17:12 +02:00
|
|
|
|
procedure TMVCController.RenderWrappedList(aList: IWrappedList;
|
|
|
|
|
aJSONObjectActionProc: TJSONObjectActionProc = nil;
|
|
|
|
|
aSerializationType: TDMVCSerializationType = TDMVCSerializationType.
|
|
|
|
|
Properties);
|
|
|
|
|
var
|
|
|
|
|
JSON: TJSONArray;
|
|
|
|
|
begin
|
|
|
|
|
if aSerializationType = TSerializationType.Properties then
|
|
|
|
|
JSON := Mapper.ObjectListToJSONArray(aList, true,
|
|
|
|
|
aJSONObjectActionProc)
|
|
|
|
|
else
|
|
|
|
|
JSON := Mapper.ObjectListToJSONArrayFields(aList, true,
|
|
|
|
|
aJSONObjectActionProc);
|
|
|
|
|
Render(JSON, true);
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.Render<T>(aCollection: TObjectList<T>;
|
|
|
|
|
aInstanceOwner: Boolean; aJSONObjectActionProc: TJSONObjectActionProc;
|
|
|
|
|
aSerializationType: TSerializationType);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
JSON: TJSONArray;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if aSerializationType = TSerializationType.Properties then
|
|
|
|
|
JSON := Mapper.ObjectListToJSONArray<T>(aCollection, false,
|
|
|
|
|
aJSONObjectActionProc)
|
2015-12-02 04:14:15 +01:00
|
|
|
|
else
|
2016-06-22 17:49:16 +02:00
|
|
|
|
JSON := Mapper.ObjectListToJSONArrayFields<T>(aCollection, false,
|
|
|
|
|
aJSONObjectActionProc);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Render(JSON, true);
|
2016-06-22 17:49:16 +02:00
|
|
|
|
if aInstanceOwner then
|
|
|
|
|
FreeAndNil(aCollection);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.RenderJSONArrayAsProperty(const aPropertyName: string;
|
2016-05-11 10:39:20 +02:00
|
|
|
|
AJSONArray: TJSONArray);
|
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Render(TJSONObject.Create(TJSONPair.Create(aPropertyName,
|
2016-05-11 10:39:20 +02:00
|
|
|
|
AJSONArray)));
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.RenderListAsProperty<T>(const aPropertyName: string;
|
|
|
|
|
aObjectList: TObjectList<T>; aOwnsInstance: Boolean;
|
|
|
|
|
aJSONObjectActionProc: TJSONObjectActionProc);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Render(TJSONObject.Create(TJSONPair.Create(aPropertyName,
|
|
|
|
|
Mapper.ObjectListToJSONArray<T>(aObjectList, aOwnsInstance,
|
|
|
|
|
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
|
2015-12-02 04:14:15 +01:00
|
|
|
|
SendStream(AStream);
|
2014-04-15 17:03:47 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.Render(aTextWriter: TTextWriter; aInstanceOwner: Boolean);
|
2016-04-22 09:46:21 +02:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
InternalRenderText(aTextWriter.ToString, ContentType, ContentCharset, Context);
|
2016-04-22 09:46:21 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.Render(aJSONValue: TJSONValue;
|
|
|
|
|
aInstanceOwner: Boolean);
|
2013-12-04 13:06:18 +01:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
InternalRender(aJSONValue, ContentType, ContentCharset, Context,
|
|
|
|
|
aInstanceOwner);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-10-18 16:35:50 +02:00
|
|
|
|
procedure TMVCController.ResponseStatusCode(const AStatusCode: UInt16;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
AStatusText: string);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Context.Response.StatusCode := AStatusCode;
|
|
|
|
|
Context.Response.ReasonString := AStatusText;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function TMVCController.ResponseStream: TStringBuilder;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if not Assigned(FResponseStream) then
|
|
|
|
|
FResponseStream := TStringBuilder.Create;
|
|
|
|
|
Result := FResponseStream;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
constructor MVCPathAttribute.Create;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Create('');
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.Render(const aErrorCode: UInt16;
|
|
|
|
|
aJSONValue: TJSONValue; aInstanceOwner: Boolean);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
ResponseStatusCode(aErrorCode);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if ContentType = 'application/json' then
|
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Render(aJSONValue, aInstanceOwner);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
raise EMVCException.Create
|
|
|
|
|
('Cannot render a JSONValue if ContentType is not application/json');
|
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
procedure TMVCController.Render(const aErrorCode: UInt16; aObject: TObject;
|
|
|
|
|
aInstanceOwner: Boolean);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2016-06-22 17:49:16 +02:00
|
|
|
|
Render(aErrorCode, Mapper.ObjectToJSONObject(aObject), true);
|
|
|
|
|
if aInstanceOwner then
|
|
|
|
|
aObject.Free;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TMVCController.Render;
|
|
|
|
|
begin
|
2016-09-25 16:17:37 +02:00
|
|
|
|
RenderResponseStream;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ MVCStringAttribute }
|
|
|
|
|
|
2014-04-16 22:52:25 +02:00
|
|
|
|
constructor MVCStringAttribute.Create(const Value: string);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited Create;
|
|
|
|
|
FValue := Value;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function IsShuttingDown: Boolean;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := TInterlocked.Read(_IsShuttingDown) = 1
|
2013-10-30 00:48:23 +01:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure EnterInShutdownState;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
TInterlocked.Add(_IsShuttingDown, 1);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
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
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Create(Value);
|
|
|
|
|
FProduceEncoding := ProduceEncoding;
|
2013-11-08 23:10:25 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-04-16 22:52:25 +02:00
|
|
|
|
constructor MVCProducesAttribute.Create(const Value: string);
|
2013-11-08 23:10:25 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited;
|
|
|
|
|
FProduceEncoding := 'UTF-8';
|
2013-11-08 23:10:25 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2014-05-21 17:16:15 +02:00
|
|
|
|
procedure MVCProducesAttribute.SetProduceEncoding(const Value: string);
|
2013-11-08 23:10:25 +01:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FProduceEncoding := Value;
|
2013-11-08 23:10:25 +01:00
|
|
|
|
end;
|
|
|
|
|
|
2015-04-01 17:01:23 +02:00
|
|
|
|
{ TUser }
|
|
|
|
|
|
|
|
|
|
procedure TUser.Clear;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FUserName := '';
|
|
|
|
|
FLoggedSince := 0;
|
|
|
|
|
FRealm := '';
|
|
|
|
|
FRoles.Clear;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
constructor TUser.Create;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
inherited;
|
|
|
|
|
FRoles := TList<string>.Create;
|
|
|
|
|
Clear;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
destructor TUser.Destroy;
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FRoles.Free;
|
|
|
|
|
inherited;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TUser.GetIsValidLoggedUser: Boolean;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
Result := (not UserName.IsEmpty) and (LoggedSince > 0);
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-06-22 17:49:16 +02:00
|
|
|
|
function TUser.LoadFromSession(AWebSession: TWebSession): Boolean;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LSerObj: string;
|
|
|
|
|
LPieces: TArray<string>;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
I: Integer;
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
|
|
|
|
if not Assigned(AWebSession) then
|
|
|
|
|
Exit(false);
|
|
|
|
|
LSerObj := AWebSession[TMVCConstants.CURRENT_USER_SESSION_KEY];
|
|
|
|
|
Result := not LSerObj.IsEmpty;
|
|
|
|
|
if Result then
|
|
|
|
|
begin
|
|
|
|
|
Clear;
|
|
|
|
|
LPieces := LSerObj.Split(['$$'], TStringSplitOptions.None);
|
|
|
|
|
UserName := LPieces[0];
|
|
|
|
|
LoggedSince := ISOStrToDateTime(LPieces[1]);
|
|
|
|
|
Realm := LPieces[2];
|
|
|
|
|
Roles.Clear;
|
2016-09-16 23:54:54 +02:00
|
|
|
|
for I := 2 to Length(LPieces) - 1 do
|
2015-12-02 04:14:15 +01:00
|
|
|
|
begin
|
2016-09-16 23:54:54 +02:00
|
|
|
|
Roles.Add(LPieces[I]);
|
2015-12-02 04:14:15 +01:00
|
|
|
|
end;
|
|
|
|
|
end;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TUser.SaveToSession(AWebSession: TWebSession);
|
|
|
|
|
var
|
2015-12-02 04:14:15 +01:00
|
|
|
|
LRoles: string;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if FRoles.Count > 0 then // bug in string.Join
|
|
|
|
|
LRoles := string.Join('$$', FRoles.ToArray)
|
|
|
|
|
else
|
|
|
|
|
LRoles := '';
|
|
|
|
|
AWebSession[TMVCConstants.CURRENT_USER_SESSION_KEY] := FUserName + '$$' +
|
|
|
|
|
ISODateTimeToString(FLoggedSince) + '$$' + FRealm + '$$' + LRoles;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TUser.SetLoggedSince(const Value: TDateTime);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
if FLoggedSince = 0 then
|
|
|
|
|
FLoggedSince := Value
|
|
|
|
|
else
|
|
|
|
|
raise EMVCException.Create('User.LoggedSince already set');
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TUser.SetRealm(const Value: string);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FRealm := Value;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TUser.SetUserName(const Value: string);
|
|
|
|
|
begin
|
2015-12-02 04:14:15 +01:00
|
|
|
|
FUserName := Value;
|
2015-04-01 17:01:23 +02:00
|
|
|
|
end;
|
|
|
|
|
|
2016-02-29 13:48:36 +01:00
|
|
|
|
{ TMVCControllerRoutable }
|
|
|
|
|
|
2016-03-01 22:50:32 +01:00
|
|
|
|
constructor TMVCControllerRoutable.Create(AClass: TMVCControllerClass;
|
|
|
|
|
ADelegate: TMVCControllerDelegate);
|
2016-02-29 13:48:36 +01:00
|
|
|
|
begin
|
|
|
|
|
FClass := AClass;
|
|
|
|
|
FDelegate := ADelegate;
|
|
|
|
|
end;
|
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
|
initialization
|
|
|
|
|
|
|
|
|
|
_IsShuttingDown := 0;
|
|
|
|
|
|
|
|
|
|
end.
|