Skip to content

Commit

Permalink
NuGet v6.2.0, add exception handlers to static, content, parameter, a…
Browse files Browse the repository at this point in the history
…nd dynamic routes
  • Loading branch information
jchristn committed Jun 30, 2024
1 parent 060f6df commit bc53db0
Show file tree
Hide file tree
Showing 23 changed files with 551 additions and 159 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

## Current Version

v6.1.x
v6.2.x

- Breaking change to move ```ContentRouteHandler``` into ```ContentRouteManager```
- Support for specifying exception handler for static, content, parameter, and dynamic routes (thank you @nomadeon)

## Previous Versions

v6.1.x

- Breaking change to move ```ContentRouteHandler``` into ```ContentRouteManager```

v6.0.x

- Major refactor with breaking changes to consolidate WatsonWebserver and HttpServerLite
Expand Down
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ Special thanks to @DamienDennehy for allowing us the use of the ```Watson.Core``

This project is part of the [.NET Foundation](http://www.dotnetfoundation.org/projects) along with other projects like [the .NET Runtime](https://github.com/dotnet/runtime/).

## New in v6.1.x
## New in v6.2.x

- Breaking change to move ```ContentRouteHandler``` into ```ContentRouteManager```
- Support for specifying exception handler for static, content, parameter, and dynamic routes (thank you @nomadeon)

## Special Thanks

Expand All @@ -28,7 +28,7 @@ I'd like to extend a special thanks to those that have helped make Watson Webser
- @MartyIX @pocsuka @orinem @deathbull @binozo @panboy75 @iain-cyborn @gamerhost31
- @nhaberl @grgouala @sapurtcomputer30 @winkmichael @sqlnew @SaintedPsycho @Return25
- @marcussacana @samisil @Jump-Suit @ChZhongPengCheng33 @bobaoapae @rodgers-r
- @john144 @zedle @GitHubProUser67 @bemoty @bemon
- @john144 @zedle @GitHubProUser67 @bemoty @bemon @nomadeon

## Watson vs Watson.Lite

Expand Down Expand Up @@ -158,6 +158,20 @@ static async Task DefaultRoute(HttpContextBase ctx) =>
await ctx.Response.Send("Hello from the default route!");
```

## Route with Exception Handler

```csharp
server.Routes.PreAuthentication.Static.Add(HttpMethod.GET, "/hello/", GetHelloRoute, MyExceptionRoute);

static async Task GetHelloRoute(HttpContextBase ctx) => throw new Exception("Whoops!");

static async Task MyExceptionRoute(HttpContextBase ctx, Exception e)
{
ctx.Response.StatusCode = 500;
await ctx.Response.Send(e.Message);
}
```

## Permit or Deny by IP or Network

```csharp
Expand Down
24 changes: 12 additions & 12 deletions src/Test.Default/Program.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
using GetSomeInput;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using WatsonWebserver;
using WatsonWebserver.Core;
using WatsonWebserver.Lite;

namespace Test
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using WatsonWebserver;
using WatsonWebserver.Core;
using WatsonWebserver.Lite;

static class Program
{
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously

static bool _UsingLite = true;
static bool _UsingLite = false;
static string _Hostname = "localhost";
static int _Port = 8080;
static WebserverSettings _Settings = null;
Expand Down Expand Up @@ -73,7 +73,7 @@ static void Main()
_Server.Routes.PreAuthentication.Parameter.Matcher.Logger = Console.WriteLine;
_Server.Routes.PreAuthentication.Parameter.Add(HttpMethod.GET, "/user/{id}", GetUserByIdRoute);
_Server.Routes.PreAuthentication.Parameter.Add(HttpMethod.GET, "/{version}/param1/{id}", ParameterRoute1);
_Server.Routes.PreAuthentication.Parameter.Add(HttpMethod.GET, "/{version}/param1/{id}", ParameterRoute2, Guid.NewGuid(), "TestMetadata");
_Server.Routes.PreAuthentication.Parameter.Add(HttpMethod.GET, "/{version}/param1/{id}", ParameterRoute2, null, Guid.NewGuid(), "TestMetadata");

_Server.Routes.PreAuthentication.Dynamic.Add(HttpMethod.GET, new Regex("^/bar$"), BarRoute);
_Server.Routes.PreAuthentication.Dynamic.Add(HttpMethod.PUT, new Regex("^/foo$"), FooWithoutIdRoute);
Expand Down
24 changes: 12 additions & 12 deletions src/Test.HostBuilder/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,39 +44,39 @@ public static void Main(string[] args)
Console.WriteLine("| Responding from pre-authentication static route /preauth/static");
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapParameterRoute(HttpMethod.GET, "/preauth/parameter/{id}", async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from pre-authentication parameter route /preauth/parameter/" + ctx.Request.Url.Parameters["id"]);
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapDynamicRoute(HttpMethod.GET, new Regex("^/preauth/dynamic/\\d+$"), async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from pre-authentication dynamic route /preauth/dynamic");
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapAuthenticationRoute(AuthenticationRoute)
.MapContentRoute("/postauth/content", false, true)
.MapStaticRoute(HttpMethod.GET, "/postauth/static", async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from post-authentication static route /postauth/static");
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapParameterRoute(HttpMethod.GET, "/postauth/parameter/{id}", async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from post-authentication parameter route /postauth/parameter/" + ctx.Request.Url.Parameters["id"]);
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapDynamicRoute(HttpMethod.GET, new Regex("^/postauth/dynamic/\\d+$"), async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from post-authentication dynamic route /postauth/dynamic");
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapPostRoutingRoute(PostRoutingHandler)
.Build();
}
Expand All @@ -91,39 +91,39 @@ public static void Main(string[] args)
Console.WriteLine("| Responding from pre-authentication static route /preauth/static");
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapParameterRoute(HttpMethod.GET, "/preauth/parameter/{id}", async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from pre-authentication parameter route /preauth/parameter/" + ctx.Request.Url.Parameters["id"]);
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapDynamicRoute(HttpMethod.GET, new Regex("^/preauth/dynamic/\\d+$"), async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from pre-authentication dynamic route /preauth/dynamic");
await ctx.Response.Send();
return;
}, false)
}, null, false)
.MapAuthenticationRoute(AuthenticationRoute)
.MapContentRoute("/postauth/content", false, true)
.MapStaticRoute(HttpMethod.GET, "/postauth/static", async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from post-authentication static route /postauth/static");
await ctx.Response.Send();
return;
}, true)
}, null, true)
.MapParameterRoute(HttpMethod.GET, "/postauth/parameter/{id}", async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from post-authentication parameter route /postauth/parameter/" + ctx.Request.Url.Parameters["id"]);
await ctx.Response.Send();
return;
}, true)
}, null, true)
.MapDynamicRoute(HttpMethod.GET, new Regex("^/postauth/dynamic/\\d+$"), async (HttpContextBase ctx) =>
{
Console.WriteLine("| Responding from post-authentication dynamic route /postauth/dynamic");
await ctx.Response.Send();
return;
}, true)
}, null, true)
.MapPostRoutingRoute(PostRoutingHandler)
.Build();
}
Expand Down
16 changes: 15 additions & 1 deletion src/WatsonWebserver.Core/ContentRoute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public class ContentRoute
[JsonPropertyOrder(1)]
public bool IsDirectory { get; set; } = false;

/// <summary>
/// The handler to invoke when exceptions are raised.
/// </summary>
[JsonIgnore]
public Func<HttpContextBase, Exception, Task> ExceptionHandler { get; set; } = null;

/// <summary>
/// User-supplied metadata.
/// </summary>
Expand All @@ -53,16 +59,24 @@ public class ContentRoute
/// </summary>
/// <param name="path">The pattern against which the raw URL should be matched.</param>
/// <param name="isDirectory">Indicates whether or not the path specifies a directory. If so, any matching URL will be handled by the specified handler.</param>
/// <param name="exceptionHandler">The method that should be called to handle exceptions.</param>
/// <param name="guid">Globally-unique identifier.</param>
/// <param name="metadata">User-supplied metadata.</param>
public ContentRoute(string path, bool isDirectory, Guid guid = default(Guid), object metadata = null)
public ContentRoute(
string path,
bool isDirectory,
Func<HttpContextBase, Exception, Task> exceptionHandler = null,
Guid guid = default(Guid),
object metadata = null)
{
if (String.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
Path = path.ToLower();
IsDirectory = isDirectory;
ExceptionHandler = exceptionHandler;

if (guid == default(Guid)) GUID = Guid.NewGuid();
else GUID = guid;

if (metadata != null) Metadata = metadata;
}

Expand Down
12 changes: 9 additions & 3 deletions src/WatsonWebserver.Core/ContentRouteManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,19 @@ public ContentRouteManager()
/// Add a route.
/// </summary>
/// <param name="path">URL path, i.e. /path/to/resource.</param>
/// <param name="isDirectory">True if the path represents a directory.</param>
/// <param name="isDirectory">True if the path represents a directory.</param>
/// <param name="exceptionHandler">The method that should be called to handle exceptions.</param>
/// <param name="guid">Globally-unique identifier.</param>
/// <param name="metadata">User-supplied metadata.</param>
public void Add(string path, bool isDirectory, Guid guid = default(Guid), object metadata = null)
public void Add(
string path,
bool isDirectory,
Func<HttpContextBase, Exception, Task> exceptionHandler = null,
Guid guid = default(Guid),
object metadata = null)
{
if (String.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
Add(new ContentRoute(path, isDirectory, guid, metadata));
Add(new ContentRoute(path, isDirectory, exceptionHandler, guid, metadata));
}

/// <summary>
Expand Down
18 changes: 16 additions & 2 deletions src/WatsonWebserver.Core/DynamicRoute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ public class DynamicRoute
[JsonIgnore]
public Func<HttpContextBase, Task> Handler { get; set; } = null;

/// <summary>
/// The handler to invoke when exceptions are raised.
/// </summary>
[JsonIgnore]
public Func<HttpContextBase, Exception, Task> ExceptionHandler { get; set; } = null;

/// <summary>
/// User-supplied metadata.
/// </summary>
Expand All @@ -59,17 +65,25 @@ public class DynamicRoute
/// </summary>
/// <param name="method">The HTTP method, i.e. GET, PUT, POST, DELETE, etc.</param>
/// <param name="path">The pattern against which the raw URL should be matched.</param>
/// <param name="handler">The method that should be called to handle the request.</param>
/// <param name="handler">The method that should be called to handle the request.</param>
/// <param name="exceptionHandler">The method that should be called to handle exceptions.</param>
/// <param name="guid">Globally-unique identifier.</param>
/// <param name="metadata">User-supplied metadata.</param>
public DynamicRoute(HttpMethod method, Regex path, Func<HttpContextBase, Task> handler, Guid guid = default(Guid), object metadata = null)
public DynamicRoute(
HttpMethod method,
Regex path,
Func<HttpContextBase, Task> handler,
Func<HttpContextBase, Exception, Task> exceptionHandler = null,
Guid guid = default(Guid),
object metadata = null)
{
if (path == null) throw new ArgumentNullException(nameof(path));
if (handler == null) throw new ArgumentNullException(nameof(handler));

Method = method;
Path = path;
Handler = handler;
ExceptionHandler = exceptionHandler;

if (guid == default(Guid)) GUID = Guid.NewGuid();
else GUID = guid;
Expand Down
15 changes: 11 additions & 4 deletions src/WatsonWebserver.Core/DynamicRouteManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,30 @@ public DynamicRouteManager()
/// </summary>
/// <param name="method">The HTTP method.</param>
/// <param name="path">URL path, i.e. /path/to/resource.</param>
/// <param name="handler">Method to invoke.</param>
/// <param name="handler">Method to invoke.</param>
/// <param name="exceptionHandler">The method that should be called to handle exceptions.</param>
/// <param name="guid">Globally-unique identifier.</param>
/// <param name="metadata">User-supplied metadata.</param>
public void Add(HttpMethod method, Regex path, Func<HttpContextBase, Task> handler, Guid guid = default(Guid), object metadata = null)
public void Add(
HttpMethod method,
Regex path,
Func<HttpContextBase, Task> handler,
Func<HttpContextBase, Exception, Task> exceptionHandler = null,
Guid guid = default(Guid),
object metadata = null)
{
if (path == null) throw new ArgumentNullException(nameof(path));
if (handler == null) throw new ArgumentNullException(nameof(handler));

lock (_Lock)
{
DynamicRoute dr = new DynamicRoute(method, path, handler);
DynamicRoute dr = new DynamicRoute(method, path, handler, exceptionHandler, guid, metadata);

_Matcher.Add(
new Regex(BuildConsolidatedRegex(method, path)),
dr);

_Routes.Add(new DynamicRoute(method, path, handler, guid, metadata), handler);
_Routes.Add(new DynamicRoute(method, path, handler, exceptionHandler, guid, metadata), handler);
}
}

Expand Down
Loading

0 comments on commit bc53db0

Please sign in to comment.