Skip to content

Commit 1a8feac

Browse files
Allow custom modifications for HTTP responses
1 parent 8e4df66 commit 1a8feac

14 files changed

+419
-26
lines changed
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
using GenHTTP.Api.Infrastructure;
5+
6+
namespace GenHTTP.Api.Protocol
7+
{
8+
9+
public class ResponseModificationBuilder : IResponseModification<ResponseModificationBuilder>, IBuilder<ResponseModifications?>
10+
{
11+
private FlexibleResponseStatus? _Status;
12+
13+
private FlexibleContentType? _ContentType;
14+
15+
private List<Cookie>? _Cookies;
16+
17+
private string? _Encoding;
18+
19+
private DateTime? _ExpiryDate, _ModificationDate;
20+
21+
private Dictionary<string, string>? _Headers;
22+
23+
#region Functionality
24+
25+
public ResponseModificationBuilder Cookie(Cookie cookie)
26+
{
27+
if (_Cookies == null)
28+
{
29+
_Cookies = new();
30+
}
31+
32+
_Cookies.Add(cookie);
33+
34+
return this;
35+
}
36+
37+
public ResponseModificationBuilder Encoding(string encoding)
38+
{
39+
_Encoding = encoding;
40+
return this;
41+
}
42+
43+
public ResponseModificationBuilder Expires(DateTime expiryDate)
44+
{
45+
_ExpiryDate = expiryDate;
46+
return this;
47+
}
48+
49+
public ResponseModificationBuilder Header(string key, string value)
50+
{
51+
if (_Headers == null)
52+
{
53+
_Headers = new();
54+
}
55+
56+
_Headers[key] = value;
57+
58+
return this;
59+
}
60+
61+
public ResponseModificationBuilder Modified(DateTime modificationDate)
62+
{
63+
_ModificationDate = modificationDate;
64+
return this;
65+
}
66+
67+
public ResponseModificationBuilder Status(ResponseStatus status)
68+
{
69+
_Status = new(status);
70+
return this;
71+
}
72+
73+
public ResponseModificationBuilder Status(int status, string reason)
74+
{
75+
_Status = new(status, reason);
76+
return this;
77+
}
78+
79+
public ResponseModificationBuilder Type(FlexibleContentType contentType)
80+
{
81+
_ContentType = contentType;
82+
return this;
83+
}
84+
85+
public ResponseModifications? Build()
86+
{
87+
if ((_Status != null) || (_Encoding != null) || (null != _ContentType)
88+
|| (_ExpiryDate != null) || (_ModificationDate != null)
89+
|| (_Cookies?.Count > 0) || (_Headers?.Count > 0))
90+
{
91+
return new(_Status, _ContentType, _Cookies, _Encoding, _ExpiryDate, _ModificationDate, _Headers);
92+
}
93+
94+
return null;
95+
}
96+
97+
#endregion
98+
99+
}
100+
101+
}

API/Protocol/ResponseModifications.cs

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace GenHTTP.Api.Protocol
5+
{
6+
7+
public sealed class ResponseModifications
8+
{
9+
10+
#region Get-/Setters
11+
12+
public FlexibleResponseStatus? Status { get; }
13+
14+
public FlexibleContentType? ContentType { get; }
15+
16+
public List<Cookie>? Cookies { get; }
17+
18+
public string? Encoding { get; }
19+
20+
public DateTime? ExpiryDate { get; }
21+
22+
public DateTime? ModificationDate { get; }
23+
24+
public Dictionary<string, string>? Headers { get; }
25+
26+
#endregion
27+
28+
#region Initialization
29+
30+
public ResponseModifications(FlexibleResponseStatus? status, FlexibleContentType? contentType,
31+
List<Cookie>? cookies, string? encoding, DateTime? expiryDate, DateTime? modificationDate,
32+
Dictionary<string, string>? headers)
33+
{
34+
Status = status;
35+
36+
ContentType = contentType;
37+
Encoding = encoding;
38+
39+
ExpiryDate = expiryDate;
40+
ModificationDate = modificationDate;
41+
42+
Cookies = cookies;
43+
Headers = headers;
44+
}
45+
46+
#endregion
47+
48+
#region Functionality
49+
50+
public void Apply(IResponseBuilder builder)
51+
{
52+
if (Status != null)
53+
{
54+
builder.Status(Status.Value.RawStatus, Status.Value.Phrase);
55+
}
56+
57+
if (null != ContentType)
58+
{
59+
builder.Type(ContentType);
60+
}
61+
62+
if (Cookies?.Count > 0)
63+
{
64+
foreach (var cookie in Cookies)
65+
{
66+
builder.Cookie(cookie);
67+
}
68+
}
69+
70+
if (Encoding != null)
71+
{
72+
builder.Encoding(Encoding);
73+
}
74+
75+
if (ExpiryDate != null)
76+
{
77+
builder.Expires(ExpiryDate.Value);
78+
}
79+
80+
if (ModificationDate != null)
81+
{
82+
builder.Modified(ModificationDate.Value);
83+
}
84+
85+
if (Headers?.Count > 0)
86+
{
87+
foreach (var header in Headers)
88+
{
89+
builder.Header(header.Key, header.Value);
90+
}
91+
}
92+
}
93+
94+
#endregion
95+
96+
}
97+
98+
}

Engine/Resources/Template.html

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
font-family: Arial, Helvetica, sans-serif;
1515
background: #444444;
1616
color: white;
17-
width: 100%;
1817
}
1918

2019
hr {

Modules/Authentication.Web/Controllers/BaseController.cs

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using GenHTTP.Api.Content;
22
using GenHTTP.Api.Content.Templating;
3+
using GenHTTP.Api.Protocol;
34

45
using GenHTTP.Modules.Authentication.Web.ViewModels;
56
using GenHTTP.Modules.IO;
@@ -11,11 +12,18 @@ namespace GenHTTP.Modules.Authentication.Web.Controllers
1112
public class BaseController
1213
{
1314

14-
protected IHandlerBuilder RenderAccountEntry(string title, string buttonCaption, string? username = null, string? errorMessage = null)
15+
protected IHandlerBuilder RenderAccountEntry(string title, string buttonCaption, string? username = null, string? errorMessage = null, ResponseStatus? status = null)
1516
{
16-
return ModRazor.Page(Resource.FromAssembly("EnterAccount.cshtml"), (r, h) => new ViewModel<EnterAccountModel>(r, h, new(buttonCaption, username, errorMessage)))
17-
.AddStyle("{web-auth-resources}/style.css")
18-
.Title(title);
17+
var response = ModRazor.Page(Resource.FromAssembly("EnterAccount.cshtml"), (r, h) => new ViewModel<EnterAccountModel>(r, h, new(buttonCaption, username, errorMessage)))
18+
.AddStyle("{web-auth-resources}/style.css")
19+
.Title(title);
20+
21+
if (status != null)
22+
{
23+
response.Status(status.Value);
24+
}
25+
26+
return response;
1927
}
2028

2129
}

Modules/Authentication.Web/Controllers/LoginController.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ public LoginController(Func<IRequest, string, string, ValueTask<IUser?>> perform
2424
public IHandlerBuilder Index()
2525
{
2626
// ToDo: already logged in
27-
return RenderLogin();
27+
return RenderLogin(status: ResponseStatus.Unauthorized);
2828
}
2929

3030
[ControllerAction(RequestMethod.POST)]
3131
public async Task<IHandlerBuilder> Index(string user, string password, IRequest request)
3232
{
3333
if (string.IsNullOrWhiteSpace(user) || string.IsNullOrWhiteSpace(password))
3434
{
35-
return RenderLogin(user, "Please enter username and password.");
35+
return RenderLogin(user, "Please enter username and password.", status: ResponseStatus.BadRequest);
3636
}
3737

3838
var authenticatedUser = await PerformLogin(request, user, password);
@@ -45,11 +45,12 @@ public async Task<IHandlerBuilder> Index(string user, string password, IRequest
4545
}
4646
else
4747
{
48-
return RenderLogin(user, "Invalid username or password.");
48+
return RenderLogin(user, "Invalid username or password.", status: ResponseStatus.Forbidden);
4949
}
5050
}
5151

52-
private IHandlerBuilder RenderLogin(string? username = null, string? errorMessage = null) => RenderAccountEntry("Login", "Login", username, errorMessage);
52+
private IHandlerBuilder RenderLogin(string? username = null, string? errorMessage = null, ResponseStatus? status = null)
53+
=> RenderAccountEntry("Login", "Login", username, errorMessage, status);
5354

5455
}
5556

Modules/Authentication.Web/Controllers/SetupController.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,16 @@ public async Task<IHandlerBuilder> Index(string user, string password, IRequest
3030
{
3131
if (string.IsNullOrWhiteSpace(user) || string.IsNullOrWhiteSpace(password))
3232
{
33-
return RenderSetup(user, "Please enter username and password.");
33+
return RenderSetup(user, "Please enter username and password.", ResponseStatus.BadRequest);
3434
}
3535

3636
await PerformSetup(request, user, password);
3737

3838
return Redirect.To("{web-auth}/", true);
3939
}
4040

41-
private IHandlerBuilder RenderSetup(string? username = null, string? errorMessage = null) => RenderAccountEntry("Setup", "Create Account", username, errorMessage);
41+
private IHandlerBuilder RenderSetup(string? username = null, string? errorMessage = null, ResponseStatus? status = null)
42+
=> RenderAccountEntry("Setup", "Create Account", username, errorMessage, status);
4243

4344
}
4445

Modules/Markdown/MarkdownPageProvider.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,19 @@ public sealed class MarkdownPageProvider<T> : IHandler where T : class, IModel
2525

2626
public PageAdditions? Additions { get; }
2727

28+
public ResponseModifications? Modifications { get; }
29+
2830
#endregion
2931

3032
#region Initialization
3133

32-
public MarkdownPageProvider(IHandler parent, IResource fileProvider, ContentInfo pageInfo, PageAdditions? additions)
34+
public MarkdownPageProvider(IHandler parent, IResource fileProvider, ContentInfo pageInfo, PageAdditions? additions, ResponseModifications? modifications)
3335
{
3436
Parent = parent;
3537

3638
PageInfo = pageInfo;
3739
Additions = additions;
40+
Modifications = modifications;
3841

3942
Renderer = new(fileProvider);
4043
}
@@ -54,7 +57,14 @@ public MarkdownPageProvider(IHandler parent, IResource fileProvider, ContentInfo
5457

5558
var templateModel = new TemplateModel(request, this, PageInfo, Additions, content);
5659

57-
return (await this.GetPageAsync(request, templateModel).ConfigureAwait(false)).Build();
60+
var response = await this.GetPageAsync(request, templateModel);
61+
62+
if (Modifications != null)
63+
{
64+
Modifications.Apply(response);
65+
}
66+
67+
return response.Build();
5868
}
5969

6070
#endregion

0 commit comments

Comments
 (0)