diff options
| author | Jake Mannens <jake@asger.xyz> | 2026-03-17 03:04:36 +1100 |
|---|---|---|
| committer | Jake Mannens <jake@asger.xyz> | 2026-06-07 12:32:37 +1000 |
| commit | c51ff4e755f009ca0bc8e935a92c04e583c4ee8a (patch) | |
| tree | 0a9a311c5404a96495df1047e613dc3aea3d0f15 /ExceptionMiddleware.cs | |
Initial commit
Diffstat (limited to 'ExceptionMiddleware.cs')
| -rw-r--r-- | ExceptionMiddleware.cs | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/ExceptionMiddleware.cs b/ExceptionMiddleware.cs new file mode 100644 index 0000000..ba4049a --- /dev/null +++ b/ExceptionMiddleware.cs @@ -0,0 +1,64 @@ +using HyperBooru.ApiModels; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace HyperBooru; + +// Middleware class to intercept API controller exceptions and +// return said exceptions to API clients as serialized JSON objects +public sealed class ExceptionMiddleware { + private RequestDelegate next; + + public ExceptionMiddleware(RequestDelegate next) => + this.next = next; + + public async Task Invoke(HttpContext context) { + try { + await next(context); + } catch(HBException e) { + context.Response.ContentType = "application/json"; + context.Response.StatusCode = + e.GetType().GetCustomAttribute<ExceptionStatusCodeAttribute>()?.StatusCode ?? + StatusCodes.Status500InternalServerError; + + context.Response.Clear(); + + await context.Response.WriteAsJsonAsync(e); + } catch(Exception) { + context.Response.StatusCode = StatusCodes.Status500InternalServerError; + context.Response.ContentType = "application/json"; + + context.Response.Clear(); + + await context.Response.WriteAsJsonAsync(new ServerException()); + } + } +} + +// This class is needed as the JSON serializer often fails to serialize +// members of the native 'Exception' class +public sealed class ExceptionJsonResolver : DefaultJsonTypeInfoResolver { + public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) { + var info = base.GetTypeInfo(type, options); + + if(!typeof(Exception).IsAssignableFrom(type)) + return info; + + string[] excludedProps = [ + "data", + "hResult", + "helpLink", + "innerException", + "source", + "stackTrace", + "targetSite" + ]; + + foreach(var p in info.Properties.Where(p => excludedProps.Contains(p.Name))) + p.ShouldSerialize = (_, _) => false; + + return info; + } +} |
