Forum Discussion

Domgrar's avatar
Domgrar
Copper Contributor
Oct 28, 2023

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!

 

 

No RepliesBe the first to reply