Производительность узких мест промежуточного ПО для ведения журнала HTTP - PullRequest
1 голос
/ 20 февраля 2020

Я попытался написать промежуточное программное обеспечение, которое регистрирует каждое тело каждого запроса и ответа, которое приложение отправляет клиенту. Однако производительность приложения сильно ухудшилась, настолько, что обычный запрос получает ответ через 80 секунд.

    public class LoggingMiddleware
    {
        private RequestDelegate _next;
        private readonly ILogger _logger;

        public LoggingMiddleware(ILogger logger, RequestDelegate next)
        {
            _logger = logger;
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            StringBuilder log = new StringBuilder();

            try
            {
                // Request
                log.Append($"A {context.Request.Method} request arrived at {context.Request.Path} {context.Request.QueryString.ToString()}");
                log.Append($"\nRequest time (local): {DateTime.Now.ToString("r")}");

                if (context.Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
                {
                    context.Request.EnableBuffering();

                    using (var reader = new StreamReader(context.Request.Body, System.Text.Encoding.UTF8, true, 1024, true))
                    {
                        string body = await reader.ReadToEndAsync();
                        string bodyLog = !string.IsNullOrEmpty(body) ? body : "EMPTY";

                        log.Append($"\nBody: {bodyLog}");
                        context.Request.Body.Seek(0, SeekOrigin.Begin);
                    }
                }

                // Response
                var originalBodyStream = context.Response.Body;
                using (var responseBody = new MemoryStream())
                {
                    context.Response.Body = responseBody;

                    // Invoke next middleware
                    await _next(context);

                    var response = context.Response;

                    response.Body.Seek(0, SeekOrigin.Begin);
                    string responseText = await new StreamReader(response.Body).ReadToEndAsync();
                    string bodyLog = !string.IsNullOrEmpty(responseText) ? responseText : "EMPTY";
                    response.Body.Seek(0, SeekOrigin.Begin);

                    log.Append($"\nResponse time (local): {DateTime.Now.ToString("r")}");
                    log.Append($"\nResponse status: {context.Response.StatusCode}");
                    log.Append($"\nBody: {bodyLog}");

                    await responseBody.CopyToAsync(originalBodyStream);
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                await Task.Run(() =>
                {
                    _logger.Log(log.ToString());
                    return Task.CompletedTask;
                });
            }
        }
    }

Я не совсем уверен, почему это так, но ясно, что я делаю что-то не так здесь. Есть ли что-то, что я могу сделать, чтобы улучшить и оптимизировать код, чтобы он работал более плавно?

Кроме того, у меня есть пользовательский атрибут, который используется поверх нескольких действий в целях проверки, и он должен генерировать ха sh содержимого тела. Производительность остается плохой даже после удаления промежуточного программного обеспечения (или атрибута), но они оба необходимы для ведения журнала и проверки.

    public class SignatureValidationAttribute : ActionFilterAttribute
    {
        public SignatureValidationAttribute() { }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            HttpRequest request = context.HttpContext.Request;
            IServiceProvider serviceProvider = context.HttpContext.RequestServices;

            if (request.Headers.TryGetValue("Signature", out StringValues values))
            {
                context.HttpContext.Request.EnableBuffering();

                using (var reader = new StreamReader(request.Body, System.Text.Encoding.UTF8, true, 1024, true))
                {
                    request.Body.Seek(0, SeekOrigin.Begin);
                    var jsonBody = reader.ReadToEnd();
                    request.Body.Seek(0, SeekOrigin.Begin);

                    var signatureService = (ISignatureService)serviceProvider.GetService(typeof(ISignatureService)); //MD5
                    string hash = signatureService.GenerateSignature(jsonBody, "SOMEKEY");

                    if (values.First() != hash)
                    {
                        context.Result = new UnauthorizedObjectResult(new
                        {
                            ErrorMessage = "Invalid hash"
                        });
                    }
                }
            }
        }
    }
...