Filrets (WIP) - refactored exception handling separating Controller exceptions from Protocol exceptions

This commit is contained in:
Daniele Teti 2023-06-23 15:43:32 +02:00
parent 4b89647ecc
commit 4e380e6ebd
7 changed files with 120 additions and 171 deletions

View File

@ -390,48 +390,18 @@ type
end;
EMVCSessionExpiredException = class(EMVCException)
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
end;
EMVCConfigException = class(EMVCException)
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
end;
EMVCFrameworkViewException = class(EMVCException)
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
end;
EMVCJWTException = class(EMVCException)
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
end;
EMVCViewError = class(EMVCException)
private
{ private declarations }
protected
{ protected declarations }
public
{ public declarations }
end;
TMVCStringDictionaryList = class;

View File

@ -52,15 +52,14 @@ procedure TMVCActionControllerFilter.DoFilter(
const Context: TWebContext;
const Router: IMVCRouter);
var
lSelectedController: TMVCController;
lControllerInstance: TMVCController;
lBodyParameter: TObject;
lActionFormalParams: TArray<TRttiParameter>;
lActualParams: TArray<TValue>;
lRouterMethodToCallName: string;
lHandled: Boolean;
begin
try
lSelectedController := Router.CreateControllerInstance;
lControllerInstance := Router.CreateControllerInstance;
except
on Ex: Exception do
begin
@ -70,51 +69,75 @@ begin
raise EMVCException.Create(http_status.InternalServerError, 'Cannot create controller');
end;
end;
TWebContextHack(Context).fActionQualifiedName := Router.ActionQualifiedName;
lSelectedController.Engine := Self.GetEngine;
TMVCControllerHack(lSelectedController).Context := Context;
lBodyParameter := nil;
TMVCControllerHack(lSelectedController).MVCControllerAfterCreate;
try
TMVCControllerHack(lSelectedController).ContentType :=
BuildContentType(Router.ResponseContentMediaType, Router.ResponseContentCharSet);
lActionFormalParams := Router.ActionMethod.GetParameters;
if (Length(lActionFormalParams) = 0) then
SetLength(lActualParams, 0)
else if (Length(lActionFormalParams) = 1) and
(SameText(lActionFormalParams[0].ParamType.QualifiedName, 'MVCFramework.TWebContext')) then
begin
SetLength(lActualParams, 1);
lActualParams[0] := Context;
end
else
begin
FillActualParamsForAction(lSelectedController, Context, lActionFormalParams, lRouterMethodToCallName,
lActualParams, lBodyParameter);
end;
TMVCControllerHack(lSelectedController).OnBeforeAction(Context, lRouterMethodToCallName, lHandled);
if not lHandled then
begin
try
Router.ActionMethod.Invoke(lSelectedController, lActualParams);
finally
TMVCControllerHack(lSelectedController).OnAfterAction(Context, lRouterMethodToCallName);
end;
end;
finally
TWebContextHack(Context).FActionQualifiedName := Router.ActionQualifiedName;
lControllerInstance.Engine := Self.GetEngine;
TMVCControllerHack(lControllerInstance).Context := Context;
lBodyParameter := nil;
TMVCControllerHack(lControllerInstance).MVCControllerAfterCreate;
try
lBodyParameter.Free;
except
on E: Exception do
TMVCControllerHack(lControllerInstance).ContentType :=
BuildContentType(Router.ResponseContentMediaType, Router.ResponseContentCharSet);
lActionFormalParams := Router.ActionMethod.GetParameters;
if (Length(lActionFormalParams) = 0) then
SetLength(lActualParams, 0)
else if (Length(lActionFormalParams) = 1) and
(SameText(lActionFormalParams[0].ParamType.QualifiedName, 'MVCFramework.TWebContext')) then
begin
LogE(Format('Cannot free Body object: [CLS: %s][MSG: %s]', [E.Classname, E.Message]));
SetLength(lActualParams, 1);
lActualParams[0] := Context;
end
else
begin
FillActualParamsForAction(lControllerInstance, Context, lActionFormalParams,
Router.ActionMethod.Name,
lActualParams, lBodyParameter);
end;
TMVCControllerHack(lControllerInstance).OnBeforeAction(Context, Router.ActionMethod.Name, lHandled);
if not lHandled then
begin
try
try
Router.ActionMethod.Invoke(lControllerInstance, lActualParams);
Context.Response.ContentType := TMVCControllerHack(lControllerInstance).ContentType;
finally
TMVCControllerHack(lControllerInstance).OnAfterAction(Context, Router.ActionMethod.Name);
end;
except
on E: EMVCException do
begin
if not GetEngine.CustomExceptionHandling(E, lControllerInstance, Context) then
begin
Log.Error('[%s] %s [PathInfo "%s"] (Custom message: "%s")',
[E.Classname, E.Message, GetRequestShortDescription(Context.Request.RawWebRequest), E.DetailedMessage], LOGGERPRO_TAG);
if Assigned(lControllerInstance) then
begin
lControllerInstance.ResponseStatus(E.HTTPErrorCode);
lControllerInstance.Render(E);
end
else
begin
GetEngine.SendRawHTTPStatus(Context, E.HTTPErrorCode, Format('[%s] %s', [E.Classname, E.Message]), E.Classname);
end;
end;
end;
end;
end;
finally
try
lBodyParameter.Free;
except
on E: Exception do
begin
LogE(Format('Cannot free Body object: [CLS: %s][MSG: %s]', [E.Classname, E.Message]));
end;
end;
TMVCControllerHack(lControllerInstance).MVCControllerBeforeDestroy;
end;
TMVCControllerHack(lSelectedController).MVCControllerBeforeDestroy;
// fOnRouterLog(lRouter, rlsRouteFound, lContext);
finally
lControllerInstance.Free;
end;
Context.Response.ContentType := TMVCControllerHack(lSelectedController).ContentType;
// fOnRouterLog(lRouter, rlsRouteFound, lContext);
end;
procedure TMVCActionControllerFilter.FillActualParamsForAction(

View File

@ -148,53 +148,53 @@ begin
end;
end; // end-execute-routing
except
on ESess: EMVCSessionExpiredException do
begin
if not fEngine.CustomExceptionHandling(ESess, lSelectedController, Context) then
begin
Log.Error('[%s] %s [PathInfo "%s"] (Custom message: "%s")',
[ESess.Classname, ESess.Message, GetRequestShortDescription(Context.Request.RawWebRequest), ESess.DetailedMessage],
LOGGERPRO_TAG);
Context.SessionStop;
lSelectedController.ResponseStatus(ESess.HTTPErrorCode);
lSelectedController.Render(ESess);
end;
end;
on E: EMVCException do
begin
if not fEngine.CustomExceptionHandling(E, lSelectedController, Context) then
begin
Log.Error('[%s] %s [PathInfo "%s"] (Custom message: "%s")',
[E.Classname, E.Message, GetRequestShortDescription(Context.Request.RawWebRequest), E.DetailedMessage], LOGGERPRO_TAG);
if Assigned(lSelectedController) then
begin
lSelectedController.ResponseStatus(E.HTTPErrorCode);
lSelectedController.Render(E);
end
else
begin
fEngine.SendRawHTTPStatus(Context, E.HTTPErrorCode, Format('[%s] %s', [E.Classname, E.Message]), E.Classname);
end;
end;
end;
on EIO: EInvalidOp do
begin
if not fEngine.CustomExceptionHandling(EIO, lSelectedController, Context) then
begin
Log.Error('[%s] %s [PathInfo "%s"] (Custom message: "%s")',
[EIO.Classname, EIO.Message, GetRequestShortDescription(Context.Request.RawWebRequest), 'Invalid Op'], LOGGERPRO_TAG);
if Assigned(lSelectedController) then
begin
lSelectedController.ResponseStatus(http_status.InternalServerError);
lSelectedController.Render(EIO);
end
else
begin
fEngine.SendRawHTTPStatus(Context, http_status.InternalServerError,
Format('[%s] %s', [EIO.Classname, EIO.Message]), EIO.Classname);
end;
end;
end;
// on ESess: EMVCSessionExpiredException do
// begin
// if not fEngine.CustomExceptionHandling(ESess, lSelectedController, Context) then
// begin
// Log.Error('[%s] %s [PathInfo "%s"] (Custom message: "%s")',
// [ESess.Classname, ESess.Message, GetRequestShortDescription(Context.Request.RawWebRequest), ESess.DetailedMessage],
// LOGGERPRO_TAG);
// Context.SessionStop;
// lSelectedController.ResponseStatus(ESess.HTTPErrorCode);
// lSelectedController.Render(ESess);
// end;
// end;
// on E: EMVCException do
// begin
// if not fEngine.CustomExceptionHandling(E, lSelectedController, Context) then
// begin
// Log.Error('[%s] %s [PathInfo "%s"] (Custom message: "%s")',
// [E.Classname, E.Message, GetRequestShortDescription(Context.Request.RawWebRequest), E.DetailedMessage], LOGGERPRO_TAG);
// if Assigned(lSelectedController) then
// begin
// lSelectedController.ResponseStatus(E.HTTPErrorCode);
// lSelectedController.Render(E);
// end
// else
// begin
// fEngine.SendRawHTTPStatus(Context, E.HTTPErrorCode, Format('[%s] %s', [E.Classname, E.Message]), E.Classname);
// end;
// end;
// end;
// on EIO: EInvalidOp do
// begin
// if not fEngine.CustomExceptionHandling(EIO, lSelectedController, Context) then
// begin
// Log.Error('[%s] %s [PathInfo "%s"] (Custom message: "%s")',
// [EIO.Classname, EIO.Message, GetRequestShortDescription(Context.Request.RawWebRequest), 'Invalid Op'], LOGGERPRO_TAG);
// if Assigned(lSelectedController) then
// begin
// lSelectedController.ResponseStatus(http_status.InternalServerError);
// lSelectedController.Render(EIO);
// end
// else
// begin
// fEngine.SendRawHTTPStatus(Context, http_status.InternalServerError,
// Format('[%s] %s', [EIO.Classname, EIO.Message]), EIO.Classname);
// end;
// end;
// end;
on Ex: Exception do
begin
if not fEngine.CustomExceptionHandling(Ex, lSelectedController, Context) then

View File

@ -526,7 +526,7 @@ type
FLoggedUser: TUser;
FWebSession: TWebSession;
FData: TMVCStringDictionary;
fIntfObject: IInterface;
FIntfObject: IInterface;
function GetWebSession: TWebSession;
function GetLoggedUser: TUser;
function GetParamsTable: TMVCRequestParamsTable;
@ -535,7 +535,7 @@ type
function GetIntfObject: IInterface;
procedure SetIntfObject(const Value: IInterface);
protected
fActionQualifiedName: String;
FActionQualifiedName: String;
procedure Flush; virtual;
procedure BindToSession(const ASessionId: string);
function SendSessionCookie(const AContext: TWebContext): string;
@ -578,11 +578,8 @@ type
TMVCBase = class
private
FEngine: TMVCEngine;
FApplicationSession: TWebApplicationSession;
function GetEngine: TMVCEngine;
function GetConfig: TMVCConfig;
function GetApplicationSession: TWebApplicationSession;
procedure SetApplicationSession(const AValue: TWebApplicationSession);
procedure SetEngine(const AValue: TMVCEngine);
protected
class function GetApplicationFileName: string; static;
@ -590,8 +587,6 @@ type
public
property Engine: TMVCEngine read GetEngine write SetEngine;
property Config: TMVCConfig read GetConfig;
property ApplicationSession: TWebApplicationSession read GetApplicationSession
write SetApplicationSession;
end;
TMVCResponse = class;
@ -2922,14 +2917,6 @@ begin
Result := IncludeTrailingPathDelimiter(ExtractFilePath(GetApplicationFileName));
end;
function TMVCBase.GetApplicationSession: TWebApplicationSession;
begin
if not Assigned(FApplicationSession) then
raise EMVCException.CreateFmt('ApplicationSession not assigned to this %s instance.',
[Classname]);
Result := FApplicationSession;
end;
function TMVCBase.GetConfig: TMVCConfig;
begin
Result := Engine.Config;
@ -2942,11 +2929,6 @@ begin
Result := FEngine;
end;
procedure TMVCBase.SetApplicationSession(const AValue: TWebApplicationSession);
begin
FApplicationSession := AValue;
end;
procedure TMVCBase.SetEngine(const AValue: TMVCEngine);
begin
FEngine := AValue;
@ -3698,36 +3680,6 @@ begin
if (GetContext.Response.StatusCode = http_status.OK) then
ResponseStatus(http_status.InternalServerError, AException.Message + ' [' +
AException.Classname + ']');
// if (not GetContext.Request.IsAjax) and (GetContext.Request.ClientPrefer(TMVCMediaType.TEXT_HTML)) then
// begin
// SetContentType(TMVCMediaType.TEXT_HTML);
// Render(AException, False);
// exit;
// ResponseStream.Clear;
// ResponseStream.Append
// ('<html><head><style>pre { padding: 15px; color: #000000; background-color: #e0e0e0; }</style></head><body>')
// .Append('<h1>' + Config[TMVCConfigKey.ServerName] + ': Error Raised</h1>')
// .AppendFormat('<pre>HTTP Return Code: %d' + sLineBreak,
// [GetContext.Response.StatusCode]).AppendFormat('HTTP Reason Text: "%s"</pre>',
// [GetContext.Response.ReasonString])
// .Append('<h3><pre>').AppendFormat('Exception Class Name : %s' + sLineBreak, [AException.Classname])
// .AppendFormat('Exception Message : %s' + sLineBreak, [AException.Message]).Append('</pre></h3>');
// if Assigned(AExceptionItems) and (AExceptionItems.Count > 0) then
// begin
// ResponseStream.Append('<h2><pre>');
// for S in AExceptionItems do
// ResponseStream.AppendLine('- ' + S);
// ResponseStream.Append('</pre><h2>');
// end
// else
// begin
// ResponseStream.AppendLine('<pre>No other information available</pre>');
// end;
// ResponseStream.Append('</body></html>');
// RenderResponseStream;
// end
// else
begin
R := TMVCErrorResponse.Create;
try

View File

@ -1,2 +1,2 @@
const
DMVCFRAMEWORK_VERSION = '3.4.0-neon-beta';
DMVCFRAMEWORK_VERSION = 'x.x.x-feature-filters';

View File

@ -24,7 +24,9 @@ uses
MVCFramework.Tests.Serializer.Entities in '..\..\common\MVCFramework.Tests.Serializer.Entities.pas',
FDConnectionConfigU in '..\..\common\FDConnectionConfigU.pas',
Entities in '..\Several\Entities.pas',
EntitiesProcessors in '..\Several\EntitiesProcessors.pas';
EntitiesProcessors in '..\Several\EntitiesProcessors.pas',
MVCFramework.Filters.Action in '..\..\..\sources\MVCFramework.Filters.Action.pas',
MVCFramework.Filters.Router in '..\..\..\sources\MVCFramework.Filters.Router.pas';
{$R *.res}

View File

@ -141,6 +141,8 @@
<DCCReference Include="..\..\common\FDConnectionConfigU.pas"/>
<DCCReference Include="..\Several\Entities.pas"/>
<DCCReference Include="..\Several\EntitiesProcessors.pas"/>
<DCCReference Include="..\..\..\sources\MVCFramework.Filters.Action.pas"/>
<DCCReference Include="..\..\..\sources\MVCFramework.Filters.Router.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>