Обработка ошибок ASP.Net Core 2: как вернуть форматированные детали исключения в Http Response?

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

По умолчанию в рабочей среде ошибка 500 "Внутренняя ошибка сервера" является единственной информацией, возвращаемой API.

Это частный API, который не публикуется через Интернет, а приложение-вызывающее должно получить и сохранить все данные в случае исключения.

Сведения об исключении могут быть JSON, отформатированные в контенте HttpResponse, позволяющие вызывающему пользователю прочитать атрибут Message и атрибут StackTraceString исключения (без HTTP-страницы, такой как ConfigurationDeveloperExceptionPage).

В настоящее время метод Startup Configure по умолчанию:

public class Startup
{   
    [...]

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory)
    {
        loggerFactory.AddNLog();
        env.ConfigureNLog(Path.Combine(AppContext.BaseDirectory, "nlog.config"));

        if ( env.IsDevelopment() )
            app.UseDeveloperExceptionPage();
        else
            app.UseStatusCodePages();

        app.UseMvc();
    }
}

Ответ 1

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

public class ExceptionHandler
{
    private readonly RequestDelegate _next;

    public ExceptionHandler(RequestDelegate next)
    {
        _next = next;
    }

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

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var response = context.Response;
        response.ContentType = "application/json";
        response.StatusCode = (int)HttpStatusCode.InternalServerError;
        await response.WriteAsync(JsonConvert.SerializeObject(new
        {
            // customize as you need
            error = new
            {
                message = exception.Message,
                exception = exception.GetType().Name
            }
        }));
    }
}

и зарегистрируйте его в своем методе Startup Configure:

if (env.IsDevelopment())
    app.UseDeveloperExceptionPage();
else
   app.UseMiddleware<ExceptionHandler>();

Ответ 2

В качестве альтернативы промежуточному программному обеспечению вы можете создать ActionFilterAttribute и изменить IActionResult

Вот простой пример, объясняющий, что для всех необработанных исключений возвращается 400 Bad request с методом Exception.

public class MyUnhandledExceptionFilter : ActionFilterAttribute, IExceptionFilter
{        
    public void OnException(ExceptionContext context)
    {           
        context.Result = new BadRequestObjectResult(context.Exception.Message);
    }
}

Затем вы регистрируете его в методе Startup ConfigureServices, подобном этому

services.AddMvc(options =>
        {
            options.Filters.Add(typeof(MyUnhandledExceptionFilter));
        })

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