IRouter
This commit is contained in:
parent
9082d7a4d0
commit
7d814ee4cb
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<!--package info-->
|
||||
<PackageId>DTLib.Web</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<Version>1.1.0</Version>
|
||||
<Authors>Timerix</Authors>
|
||||
<Description>HTTP Server with simple routing</Description>
|
||||
<RepositoryType>GIT</RepositoryType>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public class DelegateRoute(Func<HttpListenerContext, Task<HttpStatusCode>> routeHandler) : Route
|
||||
public class DelegateRouteHandler(Func<HttpListenerContext, Task<HttpStatusCode>> routeHandler) : RouteHandler
|
||||
{
|
||||
public override Task<HttpStatusCode> HandleRequest(HttpListenerContext ctx) => routeHandler(ctx);
|
||||
}
|
||||
6
DTLib.Web/Routes/IRouter.cs
Normal file
6
DTLib.Web/Routes/IRouter.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public interface IRouter
|
||||
{
|
||||
Task<HttpStatusCode> Resolve(HttpListenerContext ctx);
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public abstract class Route
|
||||
public abstract class RouteHandler
|
||||
{
|
||||
public abstract Task<HttpStatusCode> HandleRequest(HttpListenerContext ctx);
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public class ServeFilesRoute(IOPath _publicDir, string _homePageUrl = "index.html") : Route
|
||||
public class ServeFilesRouteHandler(IOPath _publicDir, string _homePageUrl = "index.html") : RouteHandler
|
||||
{
|
||||
public override async Task<HttpStatusCode> HandleRequest(HttpListenerContext ctx)
|
||||
{
|
||||
48
DTLib.Web/Routes/SimpleRouter.cs
Normal file
48
DTLib.Web/Routes/SimpleRouter.cs
Normal file
@ -0,0 +1,48 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public class SimpleRouter : IRouter
|
||||
{
|
||||
/// route for base url
|
||||
public RouteHandler? HomePageRoute = null;
|
||||
/// route for any url that doesn't have its own handler
|
||||
public RouteHandler? DefaultRoute = null;
|
||||
|
||||
|
||||
private readonly Dictionary<string, RouteHandler> _routes = new();
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SimpleRouter(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public void MapRoute(string url, Func<HttpListenerContext, Task<HttpStatusCode>> route)
|
||||
=> MapRoute(url, new DelegateRouteHandler(route));
|
||||
|
||||
public void MapRoute(string url, RouteHandler route) => _routes.Add(url, route);
|
||||
|
||||
public async Task<HttpStatusCode> Resolve(HttpListenerContext ctx)
|
||||
{
|
||||
string requestPath = ctx.Request.Url?.AbsolutePath ?? "/";
|
||||
RouteHandler? route;
|
||||
if(HomePageRoute != null && requestPath == "/")
|
||||
route = HomePageRoute;
|
||||
else if (_routes.TryGetValue(requestPath, out var routeDelegate))
|
||||
route = routeDelegate;
|
||||
else route = DefaultRoute;
|
||||
|
||||
HttpStatusCode status;
|
||||
if (route == null)
|
||||
{
|
||||
_logger.LogWarn(nameof(SimpleRouter), $"couldn't resolve request path {requestPath}");
|
||||
status = HttpStatusCode.NotFound;
|
||||
}
|
||||
else status = await route.HandleRequest(ctx);
|
||||
|
||||
ctx.Response.StatusCode = (int)status;
|
||||
await ctx.Response.OutputStream.FlushAsync();
|
||||
ctx.Response.OutputStream.Close();
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@ -3,8 +3,6 @@ global using System.Collections.Generic;
|
||||
global using System.Text;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using DTLib;
|
||||
global using DTLib.Demystifier;
|
||||
global using DTLib.Filesystem;
|
||||
global using DTLib.Logging;
|
||||
global using System.Net;
|
||||
@ -12,23 +10,19 @@ using DTLib.Web.Routes;
|
||||
|
||||
namespace DTLib.Web;
|
||||
|
||||
internal class WebApp
|
||||
public class WebApp
|
||||
{
|
||||
/// route for base url
|
||||
public Route? HomePageRoute = null;
|
||||
/// route for any url that doesn't have its own handler
|
||||
public Route? DefaultRoute = null;
|
||||
|
||||
private ContextLogger _logger;
|
||||
private string _baseUrl;
|
||||
private CancellationToken _stopToken;
|
||||
private Dictionary<string, Route> routes = new();
|
||||
private readonly string _baseUrl;
|
||||
private readonly ContextLogger _logger;
|
||||
private readonly IRouter _router;
|
||||
private readonly CancellationToken _stopToken;
|
||||
|
||||
public WebApp(ILogger logger, string baseUrl, CancellationToken stopToken)
|
||||
public WebApp(string baseUrl, ILogger logger, IRouter router, CancellationToken stopToken = default)
|
||||
{
|
||||
_logger = new ContextLogger(nameof(WebApp), logger);
|
||||
_baseUrl = baseUrl;
|
||||
_stopToken = stopToken;
|
||||
_router = router;
|
||||
}
|
||||
|
||||
public async Task Run()
|
||||
@ -38,7 +32,7 @@ internal class WebApp
|
||||
server.Prefixes.Add(_baseUrl);
|
||||
server.Start();
|
||||
_logger.LogInfo("server started");
|
||||
long requestId = 0;
|
||||
long requestId = 1;
|
||||
while (!_stopToken.IsCancellationRequested)
|
||||
{
|
||||
var ctx = await server.GetContextAsync();
|
||||
@ -58,7 +52,7 @@ internal class WebApp
|
||||
try
|
||||
{
|
||||
_logger.LogInfo(logContext, $"[{ctx.Request.HttpMethod}] {ctx.Request.RawUrl} from {ctx.Request.RemoteEndPoint} ...");
|
||||
var status = await Resolve(ctx);
|
||||
var status = await _router.Resolve(ctx);
|
||||
_logger.LogInfo(logContext, status);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -67,33 +61,4 @@ internal class WebApp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void MapRoute(string url, Func<HttpListenerContext, Task<HttpStatusCode>> route)
|
||||
=> MapRoute(url, new DelegateRoute(route));
|
||||
|
||||
public void MapRoute(string url, Route route) => routes.Add(url, route);
|
||||
|
||||
public async Task<HttpStatusCode> Resolve(HttpListenerContext ctx)
|
||||
{
|
||||
string requestPath = ctx.Request.Url?.AbsolutePath ?? "/";
|
||||
Route? route;
|
||||
if(HomePageRoute != null && requestPath == "/")
|
||||
route = HomePageRoute;
|
||||
else if (routes.TryGetValue(requestPath, out var routeDelegate))
|
||||
route = routeDelegate;
|
||||
else route = DefaultRoute;
|
||||
|
||||
HttpStatusCode status;
|
||||
if (route == null)
|
||||
{
|
||||
_logger.LogWarn("couldn't resolve request path {requestPath}");
|
||||
status = HttpStatusCode.NotFound;
|
||||
}
|
||||
else status = await route.HandleRequest(ctx);
|
||||
|
||||
ctx.Response.StatusCode = (int)status;
|
||||
await ctx.Response.OutputStream.FlushAsync(_stopToken);
|
||||
ctx.Response.OutputStream.Close();
|
||||
return status;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user