ПО промежуточного слоя и страница для обработки исключений

Я новичок в концепции промежуточного программного обеспечения, и в настоящее время я пытаюсь найти правильный способ обработки исключений в моем проекте MVC Core.

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

Итак, если я хочу, чтобы этот поток происходил, следует ли мне использовать как промежуточное ПО для ведения журнала исключений, так и app.UseExceptionHandler ("/ Error"), чтобы промежуточное ПО повторно генерировало исключение для страницы? И если да, то как мне получить подробную информацию об исключении на странице ошибки? И правильно ли, что механизм обработки исключений, который я хочу перехватить первым, должен быть последним в Configure?

Все примеры, которые я нашел, имеют дело исключительно с ошибками кода состояния HTTP, такими как 404; Я хочу обрабатывать фактические исключения (и их подклассы). Таким образом, на моих страницах просмотра я могу генерировать собственные исключения, где это применимо (например, если View предоставлен null для обязательного поля).

Фрагмент Startup.cs:

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

    app.UseStatusCodePages();
    if (env.IsDevelopment() || env.IsEnvironment("qa")) {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    } else {
        app.UseExceptionHandler("/Error");
    }
    app.UseMiddleware<MyExceptionHandler>(loggerFactory);
    // ... rest of Configure is irrelevant to this issue

MyExceptionHandler.cs

using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace MyCompany.MyProject.Helpers
{
    /// <summary>
    /// A custom Exception Handler Middleware which can be re-used in other projects.
    /// </summary>
    public sealed class MyExceptionHandler
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public MyExceptionHandler(RequestDelegate next, ILoggerFactory loggerFactory) {
            _next = next;
            _logger = loggerFactory.CreateLogger<MyExceptionHandler>();
        }

        public async Task Invoke(HttpContext context) {
            try {
                await _next(context);
            } catch (Exception ex) {
                HandleException(ex);
            }
        }

        // I realize this function looks pointless, but it will have more meat to it eventually.
        public void HandleException(Exception ex) {
            if (ex is ArgumentException argEx) {
                _logger.LogError(0, argEx, argEx.Message);
            } else if (ex is InvalidOperationException ioEx) {
                _logger.LogError(0, ioEx, "An Invalid Operation Exception occurred. This is usually caused by a database call that expects "
                    + "one result, but receives none or more than one.");
            } else if (ex is SqlException sqlEx) {
                _logger.LogError(0, sqlEx, $"A SQL database exception occurred. Error Number {sqlEx.Number}");
            } else if (ex is NullReferenceException nullEx) {
                _logger.LogError(0, nullEx, $"A Null Reference Exception occurred. Source: {nullEx.Source}.");
            } else if (ex is DbUpdateConcurrencyException dbEx) {
                _logger.LogError(0, dbEx, "A database error occurred while trying to update your item. This is usually due to someone else modifying the item since you loaded it.");
            } else {
                _logger.LogError(0, ex, "An unhandled exception has occurred.")
            }
        }
    }
}

person Andrew S    schedule 27.06.2017    source источник
comment
Просто прошу хорошей меры. Вы проверили официальную документацию docs.microsoft.com/en-us/aspnet/core/fundamentals/   -  person Nkosi    schedule 28.06.2017
comment
А вот еще одна хорошая статья scottsauber.com/2017/04/03/   -  person Nkosi    schedule 28.06.2017
comment
Да, они предоставляют фрагмент кода. Вот как вы используете страницу / Error. Конец. Что хорошо, если ваша страница с ошибкой просто "Эй, случилось что-то плохое!" без настройки. Здесь говорят обработать вашу ошибку, но не указывают, как ПОЛУЧИТЬ исключение. Это в ответе? ViewBag? Я не был уверен в следующих шагах. Однако вторая ссылка более полезна. Нет ли опасений по поводу возможных исключений, которые препятствуют отображению представления ошибок?   -  person Andrew S    schedule 28.06.2017
comment
Взгляните на настраиваемое промежуточное ПО для ошибок в этой статье. dusted.codes/error-handling-in-aspnet-core   -  person Nkosi    schedule 28.06.2017
comment
Да, они конкретно упоминают попытку обработать ошибки просмотра при обработке, а затем с помощью специального обработчика, поскольку он остановит рендеринг.   -  person Nkosi    schedule 28.06.2017


Ответы (1)


должен ли я использовать как промежуточное ПО для ведения журнала исключений, так и app.UseExceptionHandler("/Error"), чтобы промежуточное ПО повторно генерировало исключение на странице?

да.

Используя только отрывок из вашего примера.

public async Task Invoke(HttpContext context) {
    try {
        await _next(context);
    } catch (Exception ex) {
        HandleException(ex);
         // re -throw the original exception
         // after logging the information
        throw;
    }
}

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

Источник Обработка ошибок в ASP.NET Core

как мне получить подробную информацию об исключении на странице ошибок?

Используйте IExceptionHandlerPathFeature, чтобы получить исключение и путь

public IActionResult Error()
{
    // Get the details of the exception that occurred
    var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();

    if (exceptionFeature != null)
    {
        // Get which route the exception occurred at
        string routeWhereExceptionOccurred = exceptionFeature.Path;

        // Get the exception that occurred
        Exception exceptionThatOccurred = exceptionFeature.Error;

        // TODO: Do something with the exception
        // Log it with Serilog?
        // Send an e-mail, text, fax, or carrier pidgeon?  Maybe all of the above?
        // Whatever you do, be careful to catch any exceptions, otherwise you'll end up with a blank page and throwing a 500
    }

    return View();
}

Источник Добавление глобальной ошибки Обработка и регистрация в ASP.NET Core с помощью IExceptionHandlerPathFeature

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

Обратите особое внимание на этот небольшой комментарий при отображении просмотров ошибок.

Что бы вы ни делали, будьте осторожны, чтобы не перехватить любые исключения, иначе вы получите пустую страницу и выбросите 500

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

person Nkosi    schedule 27.06.2017
comment
Отлично, спасибо! Я волновался, что я как-то лишний или использую плохой дизайн, пытаясь реализовать обе функции. Это будет огромным подспорьем. Промежуточное ПО в Core довольно гладкое. Облегчает выполнение многих задач по написанию кода и позволяет избежать ненужной репликации. - person Andrew S; 28.06.2017
comment
@AndrewS подключаемый характер промежуточного программного обеспечения очень полезен, и все языки, на которых я видел, его использовали. Слабое соединение делает вещи очень гибкими, СУХИМИ и ТВЕРДЫМИ. ржу не могу. Рад помочь. Удачного кодирования !!!! - person Nkosi; 28.06.2017
comment
@AndrewS и просто для удовлетворения моего OCD, ваш так называемый пользовательский обработчик исключений теперь фактически считается промежуточным программным обеспечением регистратора исключений, поскольку уже существует встроенное промежуточное программное обеспечение обработчика исключений. Я знаю, что давать имена сложно. ржу не могу. Но таким образом он четко объясняет единую ответственность. - person Nkosi; 28.06.2017
comment
Хорошая точка зрения! Моим первоначальным намерением было объединить страницу регистрации и ошибки в одно и то же промежуточное ПО, но теперь, когда я разделил проблемы, переименование имеет смысл. - person Andrew S; 28.06.2017
comment
Привет, @Nkosi, не могли бы вы помочь мне с приведенным ниже вопросом? stackoverflow.com/questions/52049727/ - person s-a-n; 28.08.2018