首页 > 解决方案 > 在运行作业的应用程序中使用中间件的最佳方法

问题描述

我有一个从外部 API 调用端点的服务。我开发了一个自定义中间件,用于处理来自这些 API 调用的可能异常并记录它们。但是,尽管中间件在Invoke 方法中包含了 try 和 catch 块,但在catch 块捕获到异常后,我的应用程序的流程会被中断。尽管应用程序继续运行,但流程只是结束了。由于此应用程序正在执行同步数据的作业,因此我需要继续流程(并记录可能的异常,正如我所提到的)。

任何人都已经经历过这种情况或有任何线索如何使流程继续?有没有更好的方法来做到这一点?我完全愿意接受建议,拜托。

这是我的代码:

public class ApiException
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<ApiException> _logger;
        private readonly ApiExceptionOptions _options;

        public ApiException(ApiExceptionOptions options, RequestDelegate next,
            ILogger<ApiException> logger)
        {
            _next = next;
            _logger = logger;
            _options = options;
        }

        public async Task Invoke(HttpContext context /* other dependencies */)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
            }
        }

        private Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            ApiError error = _options.AddResponseDetails?.Invoke(exception)
                ?? ApiErrorFactory.New(exception);

            LogApiException(exception, error);

            return CreateResponse(context, error);
        }

        private static Task CreateResponse(HttpContext context, ApiError error)
        {
            var result = JsonSerializer.Serialize(error,
                new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                });
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)error.Status;
            return context.Response.WriteAsync(result);
        }

        private void LogApiException(Exception exception, ApiError error)
        {
            var innerExMessage = GetInnermostExceptionMessage(exception);
            var level = _options.DetermineLogLevel?.Invoke(exception) ?? GetLogLevel(exception);
            _logger.Log(level, exception, "ERROR: {message} -- {ErrorId}.", innerExMessage, error.Id);
        }

        private static LogLevel GetLogLevel(Exception exception) =>
            exception switch
            {
                InvalidInputException _ => LogLevel.Warning,
                _ => LogLevel.Error
            };

        private string GetInnermostExceptionMessage(Exception exception)
        {
            if (exception.InnerException != null)
                return GetInnermostExceptionMessage(exception.InnerException);

            return exception.Message;
        }
    }

public class ApiExceptionOptions
    {        
        public Func<Exception, ApiError> AddResponseDetails { get; set; }
        
        public Func<Exception, LogLevel> DetermineLogLevel { get; set; }
    }
public static class ApiErrorFactory
    {        
        public static ApiError New(Exception exception) =>
            exception switch
            {
                InvalidInputException e => new ApiError
                {
                    Details = e.Details,
                    Message = e.Message,
                    Status = BadRequest
                },
                _ => new ApiError()
            };
    }
public class ApiError
    {
        public static string DefaultErrorMessage = "Erro no processamento da requisição.";

        public string Id { get; } = Guid.NewGuid().ToString();
        public HttpStatusCode Status { get; set; } = HttpStatusCode.InternalServerError;
        public string Title { get; set; } = "API Error";
        public string Message { get; set; } = DefaultErrorMessage;
        public IDictionary<string, object> Details { get; set; } = new Dictionary<string, object>();
    }
public static class ApiExceptionExtensions
    {        
        public static IApplicationBuilder UseApiExceptionHandler(this IApplicationBuilder builder)
        {
            var options = new ApiExceptionOptions();
            return builder.UseMiddleware<ApiException>(options);
        }
        
        public static IApplicationBuilder UseApiExceptionHandler(this IApplicationBuilder builder,
            Action<ApiExceptionOptions> configureOptions)
        {
            var options = new ApiExceptionOptions();
            configureOptions(options);

            return builder.UseMiddleware<ApiException>(options);
        }
    }

标签: c#.net.net-coreinvokeasp.net-core-middleware

解决方案


推荐阅读