Forum Discussion
Error handling design feedback
Hi, I'm looking for some feedback on an error handling system I'm working through for a Blazor Wasm/Aspnet Core server solution
The goal here is to have unhandled exceptions return a generic "Unexpected error occurred" where specific 'business-rule' exceptions return a more detailed error message, so that the client-side is able to display something actionable to the user, and if it's not something actionable for them, display the generic message.
MiddleWare:
using System.Net;
using ErrorHandlingBlazor.Server.Exceptions;
namespace ErrorHandlingBlazor.Server.Middleware;
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly IWebHostEnvironment _environment;
public ExceptionHandlingMiddleware(RequestDelegate next, IWebHostEnvironment environment)
{
_environment = environment;
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (BaseApplicationException ex)
{
await HandleKnownExceptionAsync(context, ex);
}
catch (Exception ex)
{
await HandleUnknownExceptionAsync(context, ex);
}
}
private async Task HandleKnownExceptionAsync(HttpContext context, Exception exception)
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
await context.Response.WriteAsync(exception.Message);
}
private async Task HandleUnknownExceptionAsync(HttpContext context, Exception exception)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
if (_environment.IsDevelopment())
{
await context.Response.WriteAsync(exception.StackTrace ?? exception.Message);
}
else
{
// In a real-world scenario, we'd log exception details here
await context.Response.WriteAsync("An unknown error occurred while processing your request.");
}
}
}
BaseApplicationException, so business-exceptions have something to inherit from
public class BaseApplicationException: Exception
{
public BaseApplicationException()
{
}
public BaseApplicationException(string message)
: base(message)
{
}
public BaseApplicationException(string message, Exception inner)
: base(message, inner)
{
}
}
Example of how this would work
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray();
foreach (var forecast in forecasts)
{
if (forecast.TemperatureC < -10 || forecast.TemperatureC > 50)
{
throw new ExtremeTemperatureException($"The temperature on {forecast.Date} is too extreme at {forecast.TemperatureC}°C.");
}
}
int ran = Random.Shared.Next(0, 2);
if (ran == 0)
{
throw new Exception($"Database randomly dropped out (example)");
}
return forecasts;
}
Then on the client side we can always trust and display the error message coming from the API
protected override async Task OnInitializedAsync()
{
var res = await Http.GetAsync("WeatherForecast");
var resText = await res.Content.ReadAsStringAsync();
if (!res.IsSuccessStatusCode)
{
hasError = true;
errorMessage = resText;
return;
}
// Convert resText into WeatherForecast[]
forecasts = JsonSerializer.Deserialize<WeatherForecast[]>(resText);
}
Does this approach make sense and are there any things I'm not thinking about that I should consider?
Thanks!