I want to ask you guys is this good to handle errors globally or better way is to handle them with try catch blocks. Here are 2 different ways you can handle errors:
Global handler
public class ErrorDetails : Exception
{
public int StatusCode { get; }
public override string Message { get; }
public override string ToString()
{
return JsonConvert.SerializeObject(new {Message});
}
public ErrorDetails(int statusCode, string message)
{
Message = message;
StatusCode = statusCode;
}
}
public class GlobalErrorHandler
{
private readonly RequestDelegate _next;
public GlobalErrorHandler(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (ErrorDetails ex)
{
await HandleExceptionAsync(httpContext, ex);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, ErrorDetails exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = exception.StatusCode;
return context.Response.WriteAsync(exception.ToString());
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(exception.ToString());
}
}
And then just throw errors in code or there is also second approach
public async Task<Response<User>> Create(User user)
{
try
{
var userFromDatabase = await _repository.Queryable().FirstOrDefaultAsync(x => x.Email == user.Email);
if (userFromDatabase != null) return new Response<User>("Taki użytkownik już istnieje.");
user.Password = new PasswordHasher<string?>().HashPassword(null, user.Password);
await _repository.InsertAsync(user);
return new Response<User>(user);
}
catch(Exception ex)
{
return new Response<User>($"Error: {ex.Message}");
}
}
public class Response<TEntity> where TEntity : class
{
public readonly string Message;
public readonly bool Success;
public readonly TEntity Entity;
public readonly List<TEntity> Entities;
private Response(TEntity entity, List<TEntity> entities, string message, bool success)
{
Entity = entity;
Entities = entities;
Success = success;
Message = message;
}
public Response(string message) : this(null, null, message, false) {}
public Response(TEntity entity) : this(entity, null, null, true) {}
public Response(List<TEntity> entities) : this(null, entities, null,true) {}
}
This approach is based on Response obj that is being sent to controller and then in controller based on Response<TEntity>.Success value we sent to user object or message.
My question is: is global error handling ok or it is better to use try catch in every service.
Thanks in advance :)
In my opinion global error handlers are more helpful. Especially when you have your own custom exceptions which needs to be handled in specific way. Also it would take care of any unhandled exception and handle it gracefully.