From b4414f4da217c8311037d83d008a23214627852c Mon Sep 17 00:00:00 2001 From: MattMo Date: Thu, 26 Jan 2023 10:08:56 -0800 Subject: [PATCH] Added new route exception event handler. Renamed events. Added better example code. Bumped nuget version to 1.1.2. --- Rest.Net.Example/Program.cs | 23 ++++-- .../RequestPostProcessEventHandler.cs} | 6 +- .../Events/RequestPreProcessEventHandler.cs | 15 ++++ Rest.Net/Events/RouteExceptionEventHandler.cs | 16 +++++ .../{RouteContext.cs => ListenerContext.cs} | 10 +-- Rest.Net/Rest.Net.csproj | 4 +- Rest.Net/Route.cs | 72 +++++++++---------- Rest.Net/RouteListener.cs | 23 ++++-- Rest.Net/RoutePreprocess.cs | 16 ----- 9 files changed, 112 insertions(+), 73 deletions(-) rename Rest.Net/{RoutePostprocess.cs => Events/RequestPostProcessEventHandler.cs} (51%) create mode 100644 Rest.Net/Events/RequestPreProcessEventHandler.cs create mode 100644 Rest.Net/Events/RouteExceptionEventHandler.cs rename Rest.Net/{RouteContext.cs => ListenerContext.cs} (71%) delete mode 100644 Rest.Net/RoutePreprocess.cs diff --git a/Rest.Net.Example/Program.cs b/Rest.Net.Example/Program.cs index aecad43..c478bef 100644 --- a/Rest.Net.Example/Program.cs +++ b/Rest.Net.Example/Program.cs @@ -34,29 +34,44 @@ namespace MontoyaTech.Rest.Net.Example new Route(HttpRequestMethod.Get, "/json", Json) ); + listener.RequestPreProcessEvent += (ListenerContext context) => { + Console.WriteLine("Request start: " + context.Request.RawUrl); + return true; + }; + + listener.RequestPostProcessEvent += (ListenerContext context) => + { + Console.WriteLine("Request end: " + context.Request.RawUrl); + }; + listener.Start(); + Console.WriteLine("Available routes:"); + + foreach (var route in listener.Routes) + Console.WriteLine($"- [{route.Method}] {route.Syntax}"); + Console.WriteLine($"Rest api server running at http://localhost:{listener.Port}"); listener.Block(); } - public static HttpListenerResponse Status(RouteContext context) + public static HttpListenerResponse Status(ListenerContext context) { return context.Response.WithStatus(HttpStatusCode.OK).WithText("Everything is operational. 👍"); } - public static HttpListenerResponse Add(RouteContext context, double a, double b) + public static HttpListenerResponse Add(ListenerContext context, double a, double b) { return context.Response.WithStatus(HttpStatusCode.OK).WithText((a + b).ToString()); } - public static HttpListenerResponse Signup(RouteContext context, User user) + public static HttpListenerResponse Signup(ListenerContext context, User user) { return context.Response.WithStatus(HttpStatusCode.OK).WithText("User:" + user.Name); } - public static HttpListenerResponse Json(RouteContext context) + public static HttpListenerResponse Json(ListenerContext context) { return context.Response.WithStatus(HttpStatusCode.OK).WithJson(new User("Rest.Net")); } diff --git a/Rest.Net/RoutePostprocess.cs b/Rest.Net/Events/RequestPostProcessEventHandler.cs similarity index 51% rename from Rest.Net/RoutePostprocess.cs rename to Rest.Net/Events/RequestPostProcessEventHandler.cs index 25d7b15..69d1814 100644 --- a/Rest.Net/RoutePostprocess.cs +++ b/Rest.Net/Events/RequestPostProcessEventHandler.cs @@ -4,11 +4,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace MontoyaTech.Rest.Net +namespace MontoyaTech.Rest.Net.Events { /// - /// A route post process delegate that can be used to process the response from routes. + /// A delegate to post process requests. /// /// - public delegate void RoutePostprocess(RouteContext context); + public delegate void RequestPostProcessEventHandler(ListenerContext context); } diff --git a/Rest.Net/Events/RequestPreProcessEventHandler.cs b/Rest.Net/Events/RequestPreProcessEventHandler.cs new file mode 100644 index 0000000..0c24b76 --- /dev/null +++ b/Rest.Net/Events/RequestPreProcessEventHandler.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MontoyaTech.Rest.Net.Events +{ + /// + /// A delegate that can be used to pre process requests and stop them from being handled by routes if needed. + /// + /// + /// + public delegate bool RequestPreProcessEventHandler(ListenerContext context); +} diff --git a/Rest.Net/Events/RouteExceptionEventHandler.cs b/Rest.Net/Events/RouteExceptionEventHandler.cs new file mode 100644 index 0000000..fb6ea31 --- /dev/null +++ b/Rest.Net/Events/RouteExceptionEventHandler.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MontoyaTech.Rest.Net.Events +{ + /// + /// A delegate that can be used to handle route exceptions. + /// + /// + /// + /// + public delegate void RouteExceptionEventHandler(Route route, ListenerContext context, Exception ex); +} diff --git a/Rest.Net/RouteContext.cs b/Rest.Net/ListenerContext.cs similarity index 71% rename from Rest.Net/RouteContext.cs rename to Rest.Net/ListenerContext.cs index c1eb3ef..98cca61 100644 --- a/Rest.Net/RouteContext.cs +++ b/Rest.Net/ListenerContext.cs @@ -8,10 +8,10 @@ using System.Net; namespace MontoyaTech.Rest.Net { /// - /// An outline of a Route Context which includes - /// the request and response for a route. + /// An outline of a Listener Context which includes + /// the given request and a resposne. /// - public class RouteContext + public class ListenerContext { /// /// The Http Request that requested this route. @@ -24,11 +24,11 @@ namespace MontoyaTech.Rest.Net public HttpListenerResponse Response = null; /// - /// Creates a new RouteContext with a given request and response. + /// Creates a new ListenerContext with a given request and response. /// /// /// - public RouteContext(HttpListenerRequest request, HttpListenerResponse response) + public ListenerContext(HttpListenerRequest request, HttpListenerResponse response) { this.Request = request; this.Response = response; diff --git a/Rest.Net/Rest.Net.csproj b/Rest.Net/Rest.Net.csproj index 3e554d8..5b0cfcd 100644 --- a/Rest.Net/Rest.Net.csproj +++ b/Rest.Net/Rest.Net.csproj @@ -8,7 +8,7 @@ True MontoyaTech MontoyaTech - MontoyaTech 2022 + MontoyaTech 2023 https://code.montoyatech.com/MontoyaTech/Rest.Net A simple C# library for creating a rest api. MontoyaTech;Rest.Net @@ -17,7 +17,7 @@ MontoyaTech.Rest.Net MontoyaTech.Rest.Net True - 1.1.1 + 1.1.2 HttpListener now returns NotFound if no route was found. Added WithRedirect and WithHtml extensions. diff --git a/Rest.Net/Route.cs b/Rest.Net/Route.cs index 83cdeac..f654322 100644 --- a/Rest.Net/Route.cs +++ b/Rest.Net/Route.cs @@ -25,7 +25,7 @@ namespace MontoyaTech.Rest.Net /// /// The target function to invoke if this route is invoked. /// - private Func Target; + private Func Target; /// /// Whether or not to close the response after the route is invoked. @@ -42,7 +42,7 @@ namespace MontoyaTech.Rest.Net /// /// /// - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -58,7 +58,7 @@ namespace MontoyaTech.Rest.Net /// /// /// - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } /// @@ -110,7 +110,7 @@ namespace MontoyaTech.Rest.Net /// /// /// - public virtual void Invoke(RouteContext context, params string[] arguments) + public virtual void Invoke(ListenerContext context, params string[] arguments) { this.Target.Invoke(context); } @@ -118,9 +118,9 @@ namespace MontoyaTech.Rest.Net public class Route : Route { - private Func Target; + private Func Target; - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -128,10 +128,10 @@ namespace MontoyaTech.Rest.Net this.CloseResponse = closeResponse; } - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } - public override void Invoke(RouteContext context, params string[] arguments) + public override void Invoke(ListenerContext context, params string[] arguments) { this.Target.DynamicInvoke(context, RouteArgumentConverter.Convert(arguments[0])); } @@ -139,9 +139,9 @@ namespace MontoyaTech.Rest.Net public class Route : Route { - private Func Target; + private Func Target; - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -149,10 +149,10 @@ namespace MontoyaTech.Rest.Net this.CloseResponse = closeResponse; } - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } - public override void Invoke(RouteContext context, params string[] arguments) + public override void Invoke(ListenerContext context, params string[] arguments) { this.Target.DynamicInvoke( context, @@ -164,9 +164,9 @@ namespace MontoyaTech.Rest.Net public class Route : Route { - private Func Target; + private Func Target; - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -174,10 +174,10 @@ namespace MontoyaTech.Rest.Net this.CloseResponse = closeResponse; } - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } - public override void Invoke(RouteContext context, params string[] arguments) + public override void Invoke(ListenerContext context, params string[] arguments) { this.Target.DynamicInvoke( context, @@ -190,9 +190,9 @@ namespace MontoyaTech.Rest.Net public class Route : Route { - private Func Target; + private Func Target; - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -200,10 +200,10 @@ namespace MontoyaTech.Rest.Net this.CloseResponse = closeResponse; } - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } - public override void Invoke(RouteContext context, params string[] arguments) + public override void Invoke(ListenerContext context, params string[] arguments) { this.Target.DynamicInvoke( context, @@ -217,9 +217,9 @@ namespace MontoyaTech.Rest.Net public class Route : Route { - private Func Target; + private Func Target; - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -227,10 +227,10 @@ namespace MontoyaTech.Rest.Net this.CloseResponse = closeResponse; } - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } - public override void Invoke(RouteContext context, params string[] arguments) + public override void Invoke(ListenerContext context, params string[] arguments) { this.Target.DynamicInvoke( context, @@ -245,9 +245,9 @@ namespace MontoyaTech.Rest.Net public class Route : Route { - private Func Target; + private Func Target; - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -255,10 +255,10 @@ namespace MontoyaTech.Rest.Net this.CloseResponse = closeResponse; } - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } - public override void Invoke(RouteContext context, params string[] arguments) + public override void Invoke(ListenerContext context, params string[] arguments) { this.Target.DynamicInvoke( context, @@ -274,9 +274,9 @@ namespace MontoyaTech.Rest.Net public class Route : Route { - private Func Target; + private Func Target; - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -284,10 +284,10 @@ namespace MontoyaTech.Rest.Net this.CloseResponse = closeResponse; } - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } - public override void Invoke(RouteContext context, params string[] arguments) + public override void Invoke(ListenerContext context, params string[] arguments) { this.Target.DynamicInvoke( context, @@ -304,9 +304,9 @@ namespace MontoyaTech.Rest.Net public class Route : Route { - private Func Target; + private Func Target; - public Route(string method, string syntax, Func target, bool closeResponse = true) + public Route(string method, string syntax, Func target, bool closeResponse = true) { this.Method = method; this.Syntax = syntax; @@ -314,10 +314,10 @@ namespace MontoyaTech.Rest.Net this.CloseResponse = closeResponse; } - public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) + public Route(HttpRequestMethod method, string syntax, Func target, bool closeResponse = true) : this(method.ToString(), syntax, target, closeResponse) { } - public override void Invoke(RouteContext context, params string[] arguments) + public override void Invoke(ListenerContext context, params string[] arguments) { this.Target.DynamicInvoke( context, diff --git a/Rest.Net/RouteListener.cs b/Rest.Net/RouteListener.cs index 0c50ba2..cf4b7c0 100644 --- a/Rest.Net/RouteListener.cs +++ b/Rest.Net/RouteListener.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using System.Net; using System.Threading; +using MontoyaTech.Rest.Net.Events; namespace MontoyaTech.Rest.Net { @@ -32,12 +33,17 @@ namespace MontoyaTech.Rest.Net /// /// An event to preprocess routes before the route is executed. /// - public event RoutePreprocess PreprocessEvent; + public event RequestPreProcessEventHandler RequestPreProcessEvent; /// /// An event to postprocess routes before the route response is returned. /// - public event RoutePostprocess PostprocessEvent; + public event RequestPostProcessEventHandler RequestPostProcessEvent; + + /// + /// An event that is invoked when a route has an unhandled exception. + /// + public event RouteExceptionEventHandler RouteExceptionEvent; /// /// Creates a new RouteListener with the default port number. @@ -116,12 +122,12 @@ namespace MontoyaTech.Rest.Net bool close = true; string[] arguments = null; - var context = new RouteContext(ctx.Request, ctx.Response); + var context = new ListenerContext(ctx.Request, ctx.Response); //Preprocess the route context, if it returns false, then we have to not invoke the route. try { - if (this.PreprocessEvent != null && !this.PreprocessEvent.Invoke(context)) + if (this.RequestPreProcessEvent != null && !this.RequestPreProcessEvent.Invoke(context)) handled = true; } catch { } @@ -138,8 +144,11 @@ namespace MontoyaTech.Rest.Net { this.Routes[i].Invoke(context, arguments); } - catch + catch (Exception ex) { + if (this.RouteExceptionEvent != null) + this.RouteExceptionEvent.Invoke(this.Routes[i], context, ex); + ctx.Response.WithStatus(HttpStatusCode.InternalServerError); } @@ -150,8 +159,8 @@ namespace MontoyaTech.Rest.Net //Post process the route context. try { - if (this.PostprocessEvent != null) - this.PostprocessEvent.Invoke(context); + if (this.RequestPostProcessEvent != null) + this.RequestPostProcessEvent.Invoke(context); } catch { } diff --git a/Rest.Net/RoutePreprocess.cs b/Rest.Net/RoutePreprocess.cs deleted file mode 100644 index eeb517f..0000000 --- a/Rest.Net/RoutePreprocess.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MontoyaTech.Rest.Net -{ - /// - /// A route preprocess delegate that can be used to process requests before they reach a route - /// and reject them if needed. - /// - /// - /// - public delegate bool RoutePreprocess(RouteContext context); -}