-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathModuleMiddleware.cs
More file actions
114 lines (99 loc) · 3.97 KB
/
ModuleMiddleware.cs
File metadata and controls
114 lines (99 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
using ModuWeb.Extensions;
namespace ModuWeb;
/// <summary>
/// Middleware responsible for routing HTTP requests to dynamically loaded modules.
/// </summary>
public class ModuleMiddleware
{
private static ModuleMiddleware _instance;
public static ModuleMiddleware Instance
{
get => _instance ?? throw new InvalidOperationException("ModuleMiddleware is not initialized.");
set
{
if (_instance == null)
_instance = value;
}
}
private readonly RequestDelegate _next;
internal ModuleManager moduleManager => ModuleManager.Instance;
private readonly string _basePath;
/// <summary>
/// Initializes the middleware with the next delegate and the base path.
/// </summary>
/// <param name="next">The next middleware delegate in the pipeline.</param>
/// <param name="basePath">Base URL path prefix for module routing.</param>
public ModuleMiddleware(RequestDelegate next, string basePath)
{
_next = next;
_basePath = basePath == "/" ? "" : basePath;
if (_basePath.Length > 0 && !_basePath.EndsWith("/"))
_basePath += "/";
Instance = this;
}
/// <summary>
/// Returns the module based on the request URL and outputs the remaining path.
/// </summary>
/// <param name="url">Incoming request path.</param>
/// <param name="modulePath">Remaining path after the module name.</param>
/// <returns>A <see cref="ModuleBase"/>, or <c>null</c> if module not found.</returns>
public ModuleBase? GetModuleFromUrl(string? url, out string modulePath)
{
modulePath = "";
if (string.IsNullOrEmpty(url) || url == "/")
return moduleManager.GetModule("index");
url = url?.Trim('/');
if (url.StartsWith("index"))
return moduleManager.GetModule("index");
if (!url.StartsWith(_basePath))
return null;
var modulePathString = url;
if (!string.IsNullOrEmpty(_basePath))
modulePathString = url.Replace(_basePath, "", 1);
var modulePathElements = modulePathString.Split('/');
var moduleName = modulePathElements[0];
modulePath = string.Join("/", modulePathElements.Skip(1));
return moduleManager.GetModule(moduleName);
}
/// <summary>
/// Gets the module from the URL without returning the remaining path.
/// </summary>
public ModuleBase? GetModuleFromUrl(string? url) => GetModuleFromUrl(url, out _);
/// <summary>
/// Handles the incoming HTTP request and delegates it to the matched module.
/// </summary>
/// <param name="context">HTTP context of the request.</param>
public async Task InvokeAsync(HttpContext context)
{
try
{
var path = context.Request.Path;
var remote = context.Connection.RemoteIpAddress?.ToString() ?? "?";
var port = context.Connection.RemotePort;
Logger.Info($"Request {context.Request.Method.ToUpper()} {path} from {remote}:{port}");
var module = GetModuleFromUrl(path, out var modulePath);
if (module != null)
{
context.Items["ModuWeb.CurrentModule"] = module;
if (modulePath.Length == 0 && path.HasValue && !path.Value!.EndsWith('/'))
{
var redirect = path.Value + "/" + context.Request.QueryString;
context.Response.Redirect(redirect, permanent: false);
return;
}
await module.Handle(context, modulePath, context.Request.Method.ToUpper());
return;
}
await _next(context);
}
catch (Exception ex)
{
Logger.Error($"Error processing request: {ex}");
if (!context.Response.HasStarted)
{
context.Response.StatusCode = 500;
await context.Response.WriteAsync("Internal server error");
}
}
}
}