Получение пустого ответа на ядро ​​промежуточного программного обеспечения asp.net при исключении - PullRequest
0 голосов
/ 26 февраля 2019

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

Код промежуточного программного обеспечения 01:

public async Task Invoke(HttpContext context)
{
    context.Request.EnableRewind();

    var originalBodyStream = context.Response.Body;
    using (var responseBody = new MemoryStream())
    {
        try
        {
            context.Response.Body = responseBody;
            await next(context);

            context.Response.Body.Seek(0, SeekOrigin.Begin);
            var response = await new StreamReader(context.Response.Body).ReadToEndAsync();
            context.Response.Body.Seek(0, SeekOrigin.Begin);

            // Process log
             var log = new LogMetadata();
             log.RequestMethod = context.Request.Method;
             log.RequestUri = context.Request.Path.ToString();
             log.ResponseStatusCode = context.Response.StatusCode;
             log.ResponseTimestamp = DateTime.Now;
             log.ResponseContentType = context.Response.ContentType;
             log.ResponseContent = response;
             // Keep Log to text file
             CustomLogger.WriteLog(log);

            await responseBody.CopyToAsync(originalBodyStream);
        }
        catch (Exception ex)
        {
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            var jsonObject = JsonConvert.SerializeObject(My Custom Model);
            await context.Response.WriteAsync(jsonObject, Encoding.UTF8);
            return;
        }
    }
}

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

Промежуточное программное обеспечение Код 02:

 public async Task Invoke(HttpContext context)
  {
    context.Request.EnableRewind();

    try
    {
        await next(context);
    }
    catch (Exception ex)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        var jsonObject = JsonConvert.SerializeObject(My Custom Model);
        await context.Response.WriteAsync(jsonObject, Encoding.UTF8);
        return;
    }
}

Действие моего контроллера:

    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        throw new Exception("Exception Message");
    }

Теперь я хочу показать свое исключениесообщение с моим промежуточным программным обеспечением 01, но оно не работает, но работает на моем промежуточном программном обеспечении 02.

Таким образом, мое наблюдение заключается в том, что возникает проблема с чтением ответа контекста.Есть ли что-то, что я пропустил в своем коде промежуточного программного обеспечения 01?

Есть ли какой-нибудь лучший способ служить моей цели - регистрировать тело ответа, а также управлять исключениями в глобальном масштабе?

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Я думаю, что вы говорите, что этот код не отправляет свой ответ клиенту.

 catch (Exception ex)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        var jsonObject = JsonConvert.SerializeObject(My Custom Model);
        await context.Response.WriteAsync(jsonObject, Encoding.UTF8);
        return;
    }

Причина этого в том, что await context.Response.WriteAsync(jsonObject, Encoding.UTF8); не записывает исходный поток телаэто запись в поток памяти, который можно искать.Поэтому после того, как вы напишите в него, вы должны скопировать его в исходный потокПоэтому я считаю, что код должен выглядеть следующим образом:

 catch (Exception ex)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        var jsonObject = JsonConvert.SerializeObject(My Custom Model);
        await context.Response.WriteAsync(jsonObject, Encoding.UTF8);

        context.Response.Body.Seek(0, SeekOrigin.Begin);    //IMPORTANT!
        await responseBody.CopyToAsync(originalBodyStream); //IMPORTANT!
        return;
    }
0 голосов
/ 26 февраля 2019

Есть замечательная статья, подробно объясняющая вашу проблему - Использование промежуточного программного обеспечения для перехвата исключений в Asp.Net Core .

Что вам нужно помнить о промежуточном программном обеспечении, это следующее:

Промежуточное программное обеспечение добавляется в ваше приложение во время запуска, как вы видели выше.Порядок, в котором вы вызываете методы Use ..., имеет значение!Промежуточное ПО «протекает через воду» до тех пор, пока не будет выполнено либо все, либо одна из них не остановится.

Первым, что передается на ваше промежуточное ПО, является делегат запроса.Это делегат, который берет текущий объект HttpContext и выполняет его.Ваше промежуточное ПО сохраняет его при создании и использует его на шаге Invoke ().Invoke () - то, где работа сделана.Все, что вы хотите сделать с запросом / ответом как часть вашего промежуточного программного обеспечения, делается здесь.Некоторым другим использованием промежуточного программного обеспечения может быть авторизация запроса на основе заголовка или внедрение заголовка в запрос или ответ

Итак, что вы делаете, вы пишете новый тип исключения и обработчик промежуточного программного обеспечениячтобы перехватить ваше исключение:

Новый класс типов исключений:

public class HttpStatusCodeException : Exception
{
    public int StatusCode { get; set; }
    public string ContentType { get; set; } = @"text/plain";

    public HttpStatusCodeException(int statusCode)
    {
        this.StatusCode = statusCode;

    }
    public HttpStatusCodeException(int statusCode, string message) : base(message)

    {
        this.StatusCode = statusCode;
    }
    public HttpStatusCodeException(int statusCode, Exception inner) : this(statusCode, inner.ToString()) { }

    public HttpStatusCodeException(int statusCode, JObject errorObject) : this(statusCode, errorObject.ToString())

    {
        this.ContentType = @"application/json";
    }
}

И обработчик промежуточного программного обеспечения:

public class HttpStatusCodeExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<HttpStatusCodeExceptionMiddleware> _logger;

    public HttpStatusCodeExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
        _logger = loggerFactory?.CreateLogger<HttpStatusCodeExceptionMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory));
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (HttpStatusCodeException ex)
        {
            if (context.Response.HasStarted)
            {
                _logger.LogWarning("The response has already started, the http status code middleware will not be executed.");
                throw;
            }

            context.Response.Clear();
            context.Response.StatusCode = ex.StatusCode;
            context.Response.ContentType = ex.ContentType;

            await context.Response.WriteAsync(ex.Message);

            return;
        }
    }
}

// Extension method used to add the middleware to the HTTP request pipeline.
public static class HttpStatusCodeExceptionMiddlewareExtensions
{
    public static IApplicationBuilder UseHttpStatusCodeExceptionMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<HttpStatusCodeExceptionMiddleware>();
    }
}

Затем используйте ваше новое промежуточное ПО:

  public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseHttpStatusCodeExceptionMiddleware();
    }
    else
    {
        app.UseHttpStatusCodeExceptionMiddleware();
        app.UseExceptionHandler();
    }

    app.UseStaticFiles();
    app.UseMvc();
}

Конечное использование простое:

throw new HttpStatusCodeException(StatusCodes.Status400BadRequest, @"You sent bad stuff");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...