From d7702f2c23164e93b28b13f1a627c5ebb4ce8160 Mon Sep 17 00:00:00 2001 From: arya rizky Date: Fri, 15 May 2026 12:09:40 +0700 Subject: [PATCH] fix: capture exception details when response body is empty on errors When an unhandled exception occurs, the response body written to the MemoryStream is empty because ASP.NET Core's exception handler hasn't processed it yet at the point the middleware's finally block runs. This adds ExceptionMessage and ExceptionStackTrace fields to DebugEntry and captures them in the catch block, so the DebugProbe UI can display the actual exception details even when the response body is empty. Also increase the trim limit for stack traces to 4000 chars for meaningful diagnostics. Closes #36 --- .../Middleware/DebugProbeMiddleware.cs | 20 +++++++++++-------- DebugProbe.AspNetCore/Models/DebugEntry.cs | 8 ++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/DebugProbe.AspNetCore/Middleware/DebugProbeMiddleware.cs b/DebugProbe.AspNetCore/Middleware/DebugProbeMiddleware.cs index 4f1cec3..c72fa25 100644 --- a/DebugProbe.AspNetCore/Middleware/DebugProbeMiddleware.cs +++ b/DebugProbe.AspNetCore/Middleware/DebugProbeMiddleware.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using System.Text; using DebugProbe.AspNetCore.Models; using DebugProbe.AspNetCore.Options; @@ -58,14 +58,18 @@ public async Task Invoke(HttpContext context, DebugEntryStore store) var started = Stopwatch.StartNew(); var exception = false; + string? exceptionMessage = null; + string? exceptionStackTrace = null; try { await _next(context); } - catch + catch (Exception ex) { exception = true; + exceptionMessage = ex.Message; + exceptionStackTrace = ex.StackTrace; throw; } finally @@ -100,25 +104,25 @@ public async Task Invoke(HttpContext context, DebugEntryStore store) $"{context.Request.Path}{context.Request.QueryString}", RequestBody = Trim(requestBody), - // Response ResponseBody = Trim(responseBody), + // Exception + ExceptionMessage = exceptionMessage, + ExceptionStackTrace = Trim(exceptionStackTrace, max: 4000), // Headers Headers = context.Request.Headers.ToDictionary(x => x.Key, x => x.Value.ToString()), - // Other Timestamp = DateTime.UtcNow, - }); } } - private string Trim(string value, int max = 2000) + private string Trim(string? value, int max = 2000) { - if (string.IsNullOrEmpty(value)) return value; + if (string.IsNullOrEmpty(value)) return value ?? string.Empty; return value.Length <= max ? value : value.Substring(0, max); } -} \ No newline at end of file +} diff --git a/DebugProbe.AspNetCore/Models/DebugEntry.cs b/DebugProbe.AspNetCore/Models/DebugEntry.cs index fa7fe93..e101f2d 100644 --- a/DebugProbe.AspNetCore/Models/DebugEntry.cs +++ b/DebugProbe.AspNetCore/Models/DebugEntry.cs @@ -1,4 +1,4 @@ -namespace DebugProbe.AspNetCore.Models; +namespace DebugProbe.AspNetCore.Models; public class DebugEntry { @@ -19,9 +19,13 @@ public class DebugEntry public long ResponseSize { get; set; } public string ResponseBody { get; set; } = default!; + // Exception (captured when middleware catches an unhandled error) + public string? ExceptionMessage { get; set; } + public string? ExceptionStackTrace { get; set; } + // Headers public Dictionary Headers { get; set; } = new(); // Metadata public DateTimeOffset Timestamp { get; set; } -} \ No newline at end of file +}