c# - 是否可以使用 UseExceptionHandler() 和 ExceptionHandler 选项来配置 "handle web api requests specifically"?

标签 c# asp.net-core asp.net-core-webapi

我想返回有关错误的具体 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 */;
            }
        }
    });

所以我不明白如何返回到标准处理 - 调用“/Error”页面。

这是自定义中间件,可以完成我需要的所有工作,但我有神奇的 _next 委托(delegate)来完成所有工作:

// 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;
    }
}

最佳答案

您可以通过以下方式实现两全其美:

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 调用,然后做出相应 react 。

关于c# - 是否可以使用 UseExceptionHandler() 和 ExceptionHandler 选项来配置 "handle web api requests specifically"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55472678/

相关文章:

C# 从远程系统拖放

c# - 实例化一个实现通用接口(interface)的类

c# - 如何从第二个 API 调用返回相同的状态代码

asp.net-core - 项目引用使我能够使用其他依赖项目的代码,而无需明确的项目引用

c# - 在扩展中找不到 ASP.NET Core 3.0 get_HostingEnvironment() 方法

c# - 如何在 .NET Core 2.2 WebApi 和 Azure AD 2.0 中配置 Swagger?

c# - 使用来自另一个控件的值的验证规则

c# - 将一个字节转换为 8 位,并带有 true 或 false 标志

jquery - Blueimp jQuery 文件上传与 ASP.NET Core 3.1 不兼容

c# - 在 asp.net core 中使用 JavaScript Client 在 Identity Server 4 中获取范围验证错误