Skip to content

Commit 95dcd10

Browse files
committed
store response header in exception
1 parent 559e385 commit 95dcd10

File tree

5 files changed

+229
-0
lines changed

5 files changed

+229
-0
lines changed

src/Http/Header/Contracts/ReadOnlyHeadersIntf.pas

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ interface
4545
* @return boolean true if header is set
4646
*-------------------------------------*)
4747
function has(const key : shortstring) : boolean;
48+
49+
(*!------------------------------------
50+
* returns all headers as CRLF separated
51+
* string
52+
*-------------------------------------
53+
* @return string headers as string
54+
*-------------------------------------
55+
* For example
56+
* 'Accept: application/json' + CRLF +
57+
* 'Content-Type: application/json' + CRLF +
58+
* etc
59+
*-------------------------------------*)
60+
function asString() : string;
4861
end;
4962

5063
implementation

src/Http/Header/HeadersImpl.pas

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ THeaders = class(TInterfacedObject, IReadOnlyHeaders, IHeaders, IDependency,
111111
*-------------------------------------*)
112112
function writeHeaders() : IHeaders;
113113

114+
(*!------------------------------------
115+
* returns all headers as CRLF separated
116+
* string
117+
*-------------------------------------
118+
* @return string headers as string
119+
*-------------------------------------
120+
* For example
121+
* 'Accept: application/json' + CRLF +
122+
* 'Content-Type: application/json' + CRLF +
123+
* etc
124+
*-------------------------------------*)
125+
function asString() : string;
126+
114127
function clone() : ICloneable;
115128
end;
116129

@@ -337,6 +350,30 @@ THeaderRec = record
337350
result := self;
338351
end;
339352

353+
(*!------------------------------------
354+
* returns all headers as CRLF separated
355+
* string
356+
*-------------------------------------
357+
* @return string headers as string
358+
*-------------------------------------
359+
* For example
360+
* 'Accept: application/json' + CRLF +
361+
* 'Content-Type: application/json' + CRLF +
362+
* etc
363+
*-------------------------------------*)
364+
function THeaders.asString() : string;
365+
var i, len : integer;
366+
hdr : PHeaderRec;
367+
begin
368+
result := '';
369+
len := headerList.count();
370+
for i :=0 to len-1 do
371+
begin
372+
hdr := headerList.get(i);
373+
result := hdr^.key + ': ' + hdr^.value + LineEnding;
374+
end;
375+
end;
376+
340377
function THeaders.clone() : ICloneable;
341378
var i, len : integer;
342379
srcHdr, dstHdr : PHeaderRec;

src/Http/Header/RequestHeadersImpl.pas

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ TRequestHeaders = class(TInjectableObject, IReadonlyHeaders, ICloneable)
5454
*-------------------------------------*)
5555
function has(const key : shortstring) : boolean;
5656

57+
(*!------------------------------------
58+
* returns all headers as CRLF separated
59+
* string
60+
*-------------------------------------
61+
* @return string headers as string
62+
*-------------------------------------
63+
* For example
64+
* 'Accept: application/json' + CRLF +
65+
* 'Content-Type: application/json' + CRLF +
66+
* etc
67+
*-------------------------------------*)
68+
function asString() : string;
69+
5770
function clone() : ICloneable;
5871
end;
5972

@@ -137,6 +150,30 @@ implementation
137150
end
138151
end;
139152

153+
(*!------------------------------------
154+
* returns all headers as CRLF separated
155+
* string
156+
*-------------------------------------
157+
* @return string headers as string
158+
*-------------------------------------
159+
* For example
160+
* 'Accept: application/json' + CRLF +
161+
* 'Content-Type: application/json' + CRLF +
162+
* etc
163+
*-------------------------------------*)
164+
function TRequestHeaders.asString() : string;
165+
var i, len : integer;
166+
hdr : PHeaderRec;
167+
begin
168+
result := '';
169+
len := headerList.count();
170+
for i :=0 to len-1 do
171+
begin
172+
hdr := headerList.get(i);
173+
result := hdr^.key + ': ' + hdr^.value + LineEnding;
174+
end;
175+
end;
176+
140177
function TRequestHeaders.clone() : ICloneable;
141178
begin
142179
result := TRequestHeaders.create(fEnv);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{*!
2+
* Fano Web Framework (https://fanoframework.github.io)
3+
*
4+
* @link https://github.com/fanoframework/fano
5+
* @copyright Copyright (c) 2018 - 2021 Zamrony P. Juhara
6+
* @license https://github.com/fanoframework/fano/blob/master/LICENSE (MIT)
7+
*}
8+
9+
unit DecoratorMiddlewareExecutorImpl;
10+
11+
interface
12+
13+
{$MODE OBJFPC}
14+
15+
uses
16+
17+
RequestIntf,
18+
ResponseIntf,
19+
RouteHandlerIntf,
20+
MiddlewareExecutorIntf;
21+
22+
type
23+
24+
(*!------------------------------------------------
25+
* decorator class having capability to
26+
* execute middlewares stack
27+
*
28+
* @author Zamrony P. Juhara <[email protected]>
29+
*-----------------------------------------------*)
30+
TDecoratorMiddlewareExecutor = class (TInterfacedObject, IMiddlewareExecutor)
31+
protected
32+
fExecutor : IMiddlewareExecutor;
33+
public
34+
constructor create(const aExecutor : IMiddlewareExecutor);
35+
36+
function execute(
37+
const request : IRequest;
38+
const response : IResponse;
39+
const routeHandler : IRouteHandler
40+
) : IResponse; virtual;
41+
end;
42+
43+
implementation
44+
45+
constructor TDecoratorMiddlewareExecutor.create(const aExecutor : IMiddlewareExecutor);
46+
begin
47+
fExecutor := aExecutor;
48+
end;
49+
50+
function TDecoratorMiddlewareExecutor.execute(
51+
const request : IRequest;
52+
const response : IResponse;
53+
const routeHandler : IRouteHandler
54+
) : IResponse;
55+
begin
56+
result := fExecutor.execute(request, response, routeHandler);
57+
end;
58+
59+
end.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{*!
2+
* Fano Web Framework (https://fanoframework.github.io)
3+
*
4+
* @link https://github.com/fanoframework/fano
5+
* @copyright Copyright (c) 2018 - 2021 Zamrony P. Juhara
6+
* @license https://github.com/fanoframework/fano/blob/master/LICENSE (MIT)
7+
*}
8+
9+
unit WithExceptMiddlewareExecutorImpl;
10+
11+
interface
12+
13+
{$MODE OBJFPC}
14+
15+
uses
16+
17+
RequestIntf,
18+
ResponseIntf,
19+
RouteHandlerIntf,
20+
MiddlewareExecutorIntf,
21+
DecoratorMiddlewareExecutorImpl;
22+
23+
type
24+
25+
(*!------------------------------------------------
26+
* decorator class having capability to
27+
* execute middlewares stack and rethrow any exception
28+
* as EHttpException.
29+
* This class is implemented to fix issue response headers
30+
* set previously gone when exception is raised
31+
* https://github.com/fanoframework/fano/issues/17
32+
*
33+
* @author Zamrony P. Juhara <[email protected]>
34+
*-----------------------------------------------*)
35+
TWithExceptMiddlewareExecutor = class (TDecoratorMiddlewareExecutor)
36+
public
37+
function execute(
38+
const request : IRequest;
39+
const response : IResponse;
40+
const routeHandler : IRouteHandler
41+
) : IResponse; override;
42+
end;
43+
44+
implementation
45+
46+
uses
47+
48+
sysutils,
49+
EHttpExceptionImpl,
50+
EInternalServerErrorImpl;
51+
52+
function TWithExceptMiddlewareExecutor.execute(
53+
const request : IRequest;
54+
const response : IResponse;
55+
const routeHandler : IRouteHandler
56+
) : IResponse;
57+
begin
58+
try
59+
result := inherited execute(request, response, routeHandler);
60+
except
61+
on e: EHttpException do
62+
begin
63+
//re raise but make sure add response header so that
64+
//it is not gone (see Issue #17)
65+
e.headers := e.headers + response.headers.asString();
66+
raise e;
67+
end;
68+
69+
on e: Exception do
70+
begin
71+
//re raise but add response header so that
72+
//it is not gone (see Issue #17)
73+
raise EInternalServerError.create(
74+
//add original class name in exception message
75+
//so that we know type of original exception is
76+
'(' + e.ClassName . ') ' + e.Message,
77+
response.headers.asString()
78+
);
79+
end;
80+
end;
81+
end;
82+
83+
end.

0 commit comments

Comments
 (0)