public async Task Invoke(HttpContext context)
 {
     try
     {
         await _next(context);
     }
     catch (BaseApiException ex)
     {
         var statusCode = _mapper.GetExceptionHandlerReturnCode(ex);
         _logger.LogDebug(ex, "Mapped BaseApiException of type {ExceptionType} caught, decorating response status code: {StatusCode}.", ex.GetType(), statusCode.ToString());
         context.Response.StatusCode = (int)statusCode;
         throw;
     }
     catch (ApiException ex)
     {
         _logger.LogDebug(ex, "ApiException caught, decorating response status code: {StatusCode}.", ex.StatusCode.ToString());
         context.Response.StatusCode = (int)ex.StatusCode;
         throw;
     }
     catch (Exception ex)
     {
         _logger.LogDebug(ex, "Exception caught, decorating response status code: {StatusCode}.", HttpStatusCode.InternalServerError.ToString());
         context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
         throw;
     }
 }
        internal static ApiError Build <TCategoryName>(HttpContext context, Exception ex, IExceptionMapper mapper,
                                                       ILogger <TCategoryName> logger, bool isDevelopment)
        {
            context.Response.ContentType = "application/json";

            HttpStatusCode statusCode;
            int            errorCode;
            object         developerContext = null;

            switch (ex)
            {
            case BaseApiException _:
                try
                {
                    developerContext            = (ex as BaseApiException)?.DeveloperContext;
                    errorCode                   = mapper.GetErrorCode(ex as BaseApiException);
                    statusCode                  = mapper.GetExceptionHandlerReturnCode(ex as BaseApiException);
                    context.Response.StatusCode = (int)statusCode;
                    logger.LogInformation(ex,
                                          "Mapped BaseApiException of type {exceptionType} caught by ApiExceptionHandler. Will return with {statusCodeInt} {statusCodeString}. Unexpected: {unexpected}",
                                          ex.GetType(), (int)statusCode, statusCode.ToString(), false);
                }
                catch (ArgumentException)
                {
                    goto default;
                }

                break;

            case ApiException _:
                errorCode  = -2;
                statusCode = (ex as ApiException).StatusCode;
                context.Response.StatusCode = (int)statusCode;
                logger.LogInformation(ex,
                                      "ApiException caught by ApiExceptionHandler with  {statusCodeInt} {statusCodeString}. Unexpected: {unexpected}", (int)statusCode, statusCode.ToString(), false);
                break;

            default:
                errorCode  = -1;
                statusCode = HttpStatusCode.InternalServerError;
                context.Response.StatusCode = (int)statusCode;
                logger.LogError(ex,
                                "Unhandled exception of type {exceptionType} caught by ApiExceptionHandler. Will return with {statusCodeInt} {statusCodeString}. Unexpected: {unexpected}",
                                ex.GetType(), (int)statusCode, statusCode.ToString(), true);
                break;
            }

            var error = new ApiError(mapper.Options.ServiceName)
            {
                CorrelationId    = context.TraceIdentifier,
                DeveloperContext = developerContext,
                ErrorCode        = errorCode,
            };

            if (isDevelopment)
            {
                error.Message         = ex.Message;
                error.DetailedMessage = ex.ToAsyncString();
            }
            else
            {
                error.Message = Regex.Replace(statusCode.ToString(), "[a-z][A-Z]",
                                              m => m.Value[0] + " " + char.ToLower(m.Value[1]));
                error.DetailedMessage = ex.Message;
            }

            return(error);
        }
        internal static ApiError Build <TCategoryName>(HttpContext context, Exception ex, IExceptionMapper mapper,
                                                       ILogger <TCategoryName> logger, bool isDevelopment, Action <Exception>[] exceptionListeners)
        {
            //Execute custom exception handlers first.
            foreach (var customExceptionListener in exceptionListeners)
            {
                try
                {
                    customExceptionListener.Invoke(ex);
                }
                catch (Exception e)
                {
                    logger.LogWarning(e, "Custom exception listener {exceptionListener} threw an exception.", customExceptionListener.GetType().ToString());
                }
            }

            context.Response.ContentType = "application/json";

            HttpStatusCode statusCode;

            (int errorCode, string error)errorObject;
            object developerContext = null;
            object exceptionContext = new {};

            switch (ex)
            {
            case BaseApiException baseApiException:
                try
                {
                    if (mapper.Options.RespondWithDeveloperContext)
                    {
                        developerContext = baseApiException.DeveloperContext;
                    }
                    exceptionContext            = baseApiException.Context;
                    errorObject                 = mapper.GetError(baseApiException);
                    statusCode                  = mapper.GetExceptionHandlerReturnCode(baseApiException);
                    context.Response.StatusCode = (int)statusCode;
                    logger.LogInformation(ex,
                                          "Mapped BaseApiException of type {exceptionType} caught by ApiExceptionHandler. Will return with {statusCodeInt} {statusCodeString}. Unexpected: {unexpected}",
                                          ex.GetType(), (int)statusCode, statusCode.ToString(), false);
                }
                catch (ArgumentException)
                {
                    goto default;
                }

                break;

            case OperationCanceledException _:
                errorObject = (-1, "Frogvall.AspNetCore.ExceptionHandling.OperationCanceled");
                statusCode  = HttpStatusCode.InternalServerError;
                context.Response.StatusCode = (int)statusCode;
                logger.LogWarning(ex,
                                  "OperationCanceledException exception caught by ApiExceptionHandler. Will return with {statusCodeInt} {statusCodeString}. Unexpected: {unexpected}",
                                  (int)statusCode, statusCode.ToString(), true);
                break;

            default:
                errorObject = (-1, "Frogvall.AspNetCore.ExceptionHandling.InternalServerError");
                statusCode  = HttpStatusCode.InternalServerError;
                context.Response.StatusCode = (int)statusCode;
                logger.LogError(ex,
                                "Unhandled exception of type {exceptionType} caught by ApiExceptionHandler. Will return with {statusCodeInt} {statusCodeString}. Unexpected: {unexpected}",
                                ex.GetType(), (int)statusCode, statusCode.ToString(), true);
                break;
            }

            var error = new ApiError(mapper.Options.ServiceName)
            {
                CorrelationId    = context.TraceIdentifier,
                Context          = exceptionContext,
                DeveloperContext = developerContext,
                ErrorCode        = errorObject.errorCode,
                Error            = errorObject.error
            };

            if (isDevelopment)
            {
                error.Message         = ex.Message;
                error.DetailedMessage = ex.ToString();
            }
            else
            {
                error.Message = Regex.Replace(statusCode.ToString(), "[a-z][A-Z]",
                                              m => m.Value[0] + " " + char.ToLower(m.Value[1]));
                error.DetailedMessage = ex.Message;
            }

            return(error);
        }