首页 > 解决方案 > 如何从 Rest API 中过滤掉堆栈跟踪信息并简单地返回 'Status Code: 500; 内部服务器错误'?

问题描述

我有一个 .NET 5 wepapi。我创建了一个全局异常处理程序属性,并将其放在我的基本控制器类中。看起来像这样

    public override void OnException(ExceptionContext context)
    {
        // All user exceptions implement IWebApiException
        if (context.Exception is IWebApiException webApiException)
        {
            // Then return a problem detail
            ObjectResult result = new ObjectResult(new ProblemDetails
        {
            Type = webApiException.Type,
            Title = webApiException.Title ?? ReasonPhrases.GetReasonPhrase(webApiException.Status),
            Status = webApiException.Status,
            Detail = webApiException.Detail,
            Instance = webApiException.Instance,
        })
        {
           StatusCode = webApiException.Status
        };
    result.ContentTypes
          .Add(new 
     MediaTypeHeaderValue(newMicrosoft.Extensions.Primitives.StringSegment("application/problem+json")));
    
     context.Result = result;
    }
    
     base.OnException(context);
  }

这个想法是开发人员有意抛出的任何异常都将实现IWebApiException并返回标准问题详细信息。但是,随后抛出了一个未实现的异常,IWebApiException然后 Rest API 将整个堆栈跟踪返回给调用者,响应如下所示:

System.Exception: Test
   at WebApplication1.Controllers.WeatherForecastController.Get() in C:\Dev\Projects\odiam-dot-net-api-starter\API\Controllers\WeatherForecastController.cs:line 58
   at lambda_method14(Closure , Object , Object[] )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Builder.Extensions.UsePathBaseMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

相反,我希望返回一条如下所示的简单消息:

Status Code: 500; Internal Server Error

在保持全局异常属性并且不必将每个操作都包装在 try catch 和 return 中的同时,我可以完成此任务的最简单方法是什么new StatusCodeResult(StatusCodes.Status500InternalServerError))

标签: asp.net-coreasp.net-core-webapiasp.net-core-3.1

解决方案


解决方案比我预期的要简单。我最终创建了一个中间件:

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;

    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

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

    private static Task HandleException(HttpContext context, Exception ex)
    {
        // If the exception is not user based
        if (ex is not IWebAPiException)
        {
            // 500 if unexpected
            HttpStatusCode code = HttpStatusCode.InternalServerError; 
            
            context.Response.ContentType = "text/plain";
            context.Response.StatusCode = (int)code;
                
        }

        return context.Response.WriteAsync("Status Code: 500; Internal Server Error");
    }
}

在管道中注册中间件:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseCors(APIConstants.Cors.AllowAll);
        //app.UseDeveloperExceptionPage(); <= remove this line to ensure middleware is used
    }
    
app.UseMiddleware<ErrorHandlingMiddleware>();
...
}

由于所有用户抛出的异常都应该实现IWebApiException,如果异常没有实现,则返回内部服务器错误 500。


推荐阅读