Можно ли использовать UseExceptionHandler () с параметром ExceptionHandler для настройки «обработки запросов веб-API конкретно»? - PullRequest
1 голос
/ 02 апреля 2019

Я хочу вернуть конкретную информацию json об ошибке.

У меня есть решение с пользовательским промежуточным ПО, но я не могу понять, как сделать то же самое со стандартным параметром ExceptionHandler:

Я пытаюсь:

app.UseExceptionHandler(
    new ExceptionHandlerOptions() {
        ExceptionHandlingPath=new PathString("/Error"),
        ExceptionHandler = async context =>
        {
            var ex = context.Features.Get<IExceptionHandlerFeature>().Error;
            var originalFeature = context.Features.Get<IExceptionHandlerPathFeature>();
            bool isApiCall = false;
            if (originalFeature!=null && originalFeature.Path!=null && originalFeature.Path.Contains("Api/")) // TODO: regex
            {
                isApiCall = true;
            }

            if (isApiCall)
            {
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync(AspCoreManager.GetErrorActionJson(ex, "", true));
            }
            else
            {
                await /* ???  how to get the "_next" delegate (from pipeline) or how to abort a pipeline and response with an "/Error" page */;
            }
        }
    });

Так что я не понимаю, как вернуться к стандартной обработке - вызовите страницу "/ Ошибка".

А это пользовательское промежуточное ПО, которое делает все, чтоМне нужно, но там у меня есть волшебный _next делегат, который делает всю работу:

// modified and simplified https://github.com/aspnet/Diagnostics/blob/master/src/Microsoft.AspNetCore.Diagnostics/ExceptionHandler/ExceptionHandlerMiddleware.cs
public class MyExceptionHandlerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ExceptionHandlerOptions _options;
    private readonly ILogger _logger;
    private readonly Func<object, Task> _clearCacheHeadersDelegate;
    private readonly DiagnosticSource _diagnosticSource;
    private readonly ApplicationSettings applicationSettings;

    public MyExceptionHandlerMiddleware(
        RequestDelegate next,
        ILoggerFactory loggerFactory,
        IOptions<ExceptionHandlerOptions> options,
        DiagnosticSource diagnosticSource,

        )
    {
        _next = next;
        _options = options.Value;
        _logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
        _clearCacheHeadersDelegate = ClearCacheHeaders;
        _diagnosticSource = diagnosticSource;
        if (_options.ExceptionHandler == null)
        {
            _options.ExceptionHandler = _next;

        }
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            if (context.Response.HasStarted)
            {
                throw;
            }
            PathString originalPath = context.Request.Path;
            bool isApiCall = false;
            if (originalPath.HasValue && originalPath.Value.Contains("Api/")) 
            {
                isApiCall = true;
            }

            if (_options.ExceptionHandlingPath.HasValue)
            {
                context.Request.Path = _options.ExceptionHandlingPath;
            }
            try
            {
                context.Response.Clear();
                var exceptionHandlerFeature = new ExceptionHandlerFeature()
                {
                    Error = ex,
                    Path = originalPath.Value,
                };
                context.Features.Set<IExceptionHandlerFeature>(exceptionHandlerFeature);
                context.Features.Set<IExceptionHandlerPathFeature>(exceptionHandlerFeature);
                context.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; // 500
                context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);
                if (isApiCall)
                {
                    context.Response.ContentType = "application/json";
                    await context.Response.WriteAsync(AspCoreManager.GetErrorActionJson(ex));
                }
                else
                {
                    await _options.ExceptionHandler(context);
                }

                return;
            }
            catch (Exception ex2)
            {
                // Suppress secondary exceptions
            }
            finally
            {
                context.Request.Path = originalPath;
            }
            throw; // Re-throw the original if we couldn't handle it
        }
    }

    private Task ClearCacheHeaders(object state)
    {
        var response = (HttpResponse)state;
        response.Headers[HeaderNames.CacheControl] = "no-cache";
        response.Headers[HeaderNames.Pragma] = "no-cache";
        response.Headers[HeaderNames.Expires] = "-1";
        response.Headers.Remove(HeaderNames.ETag);
        return Task.CompletedTask;
    }
}

1 Ответ

3 голосов
/ 02 апреля 2019

Вы можете достичь лучшего из обоих миров с помощью чего-то вроде следующего:

const string errorPath = "/Error";

app.UseExceptionHandler(errorPath);
app.Use(async (ctx, next) =>
{
    if (ctx.Request.Path == errorPath)
    {
        var ex = ctx.Features.Get<IExceptionHandlerFeature>().Error;
        var originalFeature = ctx.Features.Get<IExceptionHandlerPathFeature>();

        if (originalFeature != null && originalFeature.Path != null && originalFeature.Path.Contains("Api/")) // TODO: regex
        {
            ctx.Response.ContentType = "application/json";
            await ctx.Response.WriteAsync(AspCoreManager.GetErrorActionJson(ex));
            return;
        }
    }

    // Request.Path is not for /Error *or* this isn't an API call.
    await next();
});

В этом примере мы повторно используем всю существующую логику UseExceptionHandler для ведения журнала, переписывания путии т. д., а затем используйте дополнительное промежуточное ПО, которое перехватывает вызовы к /Error, проверяет, является ли это вызовом API, и затем реагирует соответствующим образом.

...