.Net 8 中错误处理的新方法

作者:微信公众号:【架构师老卢】
8-2 8:45
18

概述:我们都在努力使应用程序平稳运行,您可能还记得,我之前讨论过完全最小化异常的策略。然而,即使是最健壮的代码也会遇到路上的颠簸。这就是异常处理的用武之地。ASP.NET Core 提供了各种方法来管理这些意外情况。问题是,哪种方法最好?让我们探索您的选择,并找到最适合您应用的方案。ASP.NET Core 中的异常处理在 ASP.NET Core 中实现异常处理的首选方法是使用中间件。中间件充当中介,允许您在处理 HTTP 请求之前或之后拦截和注入自定义逻辑。这种灵活性使其成为集中处理异常的理想选择。您可以通过在中间件组件中合并一个块来实现这一点。如果在请求处理过程中发生异常,块将捕获它。在这里,

我们都在努力使应用程序平稳运行,您可能还记得,我之前讨论过完全最小化异常的策略。然而,即使是最健壮的代码也会遇到路上的颠簸。这就是异常处理的用武之地。

ASP.NET Core 提供了各种方法来管理这些意外情况。问题是,哪种方法最好?让我们探索您的选择,并找到最适合您应用的方案。

ASP.NET Core 中的异常处理

在 ASP.NET Core 中实现异常处理的首选方法是使用中间件。中间件充当中介,允许您在处理 HTTP 请求之前或之后拦截和注入自定义逻辑。这种灵活性使其成为集中处理异常的理想选择。

您可以通过在中间件组件中合并一个块来实现这一点。如果在请求处理过程中发生异常,块将捕获它。在这里,您可以选择记录错误以进行进一步分析,然后向客户端返回适当的 HTTP 错误响应。

ASP.NET Core 提供了三种创建中间件的主要方法:

  1. Request Delegates: 这种方法涉及定义一个输入对象的函数。此函数表示中间件的核心逻辑,可以处理其范围内的异常。
  2. By Convention: 此方法利用了基于约定的方法。您可以在中间件类中建立一个方法,负责处理请求并根据需要处理异常。
  3. IMiddleware Interface: 这种方法在实现接口时提供了更大的控制。此接口规定了所需的方法,这些方法成为中间件逻辑和异常处理的入口点。IMiddleware

基于约定的方法要求您定义一种方法。

下面是按约定定义的示例:ExceptionHandleMiddleware

public class ExceptionHandleMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;

    public ExceptionHandleMiddleware(
        RequestDelegate next,
        ILogger<ExceptionHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception exception)
        {
            _logger.LogError(
                exception, "Exception: {Message}", exception.Message);

            var problemDetails = new ProblemDetails
            {
                Status = StatusCodes.Status500InternalServerError,
                Title = "Error"
            };

            context.Response.StatusCode =
                StatusCodes.Status500InternalServerError;

            await context.Response.WriteAsJsonAsync(problemDetails);
        }
    }
}

将捕获任何未处理的异常,并返回 Problem Details 响应。您可以决定要返回到多少信息。您还需要将此中间件添加到 ASP.NET Core 请求管道中:ExceptionHandleMiddleware

app.UseMiddleware<ExceptionHandleMiddleware>();

使用 IExceptionHandler 简化异常处理

ASP.NET Core 8 引入了接口,为异常管理带来了更简洁的方法。此接口充当处理应用程序中的异常的中心点。

内置的异常处理程序中间件依赖于 的实现来处理处理过程中发生的错误。此接口提供单一方法:

当出现异常时,该方法将接管。它的作用是确定它是否可以处理 ASP.NET Core 管道中的特定异常。如果它可以有效地解决问题,它将返回 。但是,如果异常超出了其能力范围,它将返回 。这允许您创建用于处理不同类型异常的自定义逻辑,从而提供更精细、适应性更强的错误管理方法。

下面是一个实现示例:GlobalExceptionHandle

internal sealed class GlobalExceptionHandle : IExceptionHandler
{
    private readonly ILogger<GlobalExceptionHandle> _logger;

    public GlobalExceptionHandle(ILogger<GlobalExceptionHandle> logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        _logger.LogError(
            exception, "Exception: {Message}", exception.Message);

        var problemDetails = new ProblemDetails
        {
            Status = StatusCodes.Status500InternalServerError,
            Title = "Error"
        };

        httpContext.Response.StatusCode = problemDetails.Status.Value;

        await httpContext.Response
            .WriteAsJsonAsync(problemDetails, cancellationToken);

        return true;
    }
}

添加自定义异常处理

要将您自己的实现合并到 ASP.NET Core 请求管道中,需要执行两个步骤:IExceptionHandler

  1. 依赖注入: 注册服务。这通常是使用您的方法中的方法完成的。该服务通常使用单例生存期进行注册,因此请注意注入具有不同生存期的服务,这可能会导致冲突。
  2. 中间件注册: 在请求管道中包含 。此中间件负责利用已注册的实现来处理异常。您通常会在方法中使用该方法来实现此目的。

其他注意事项: 您可能还会遇到与此过程一起使用的情况。此方法有助于为常见异常生成格式化的“问题详细信息”响应,从而改进返回给客户端的错误消息的结构和清晰度。

builder.Services.AddExceptionHandler<GlobalExceptionHandle>();  
builder.Services.AddProblemDetails();

您还需要调用以将 添加到请求管道中:

app.UseExceptionHandler();

链接异常处理程序

您可以堆叠多个实现,以创建分层的错误处理方法。这些处理程序将按照其注册顺序进行处理。这种灵活性允许对不同类型的异常进行精细控制。IExceptionHandler

例如,您可以使用异常作为流控制的一种形式。通过定义自定义异常(如 和),您可以将特定错误条件映射到适当的 HTTP 状态代码。这有助于创建更加结构化和可预测的 API 响应。

下面是一个实现示例:BadRequestExceptionHandle

internal sealed class BadRequestExceptionHandle : IExceptionHandler
{
    private readonly ILogger<BadRequestExceptionHandle> _logger;

    public BadRequestExceptionHandle(ILogger<BadRequestExceptionHandle> logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        if (exception is not BadRequestException badRequestException)
        {
            return false;
        }

        _logger.LogError(
            badRequestException,
            "Exception: {Message}",
            badRequestException.Message);

        var problemDetails = new ProblemDetails
        {
            Status = StatusCodes.Status400BadRequest,
            Title = "BadRequest",
            Detail = badRequestException.Message
        };

        httpContext.Response.StatusCode = problemDetails.Status.Value;

        await httpContext.Response
            .WriteAsJsonAsync(problemDetails, cancellationToken);

        return true;
    }
}

下面是一个实现示例:NotFoundExceptionHandle

internal sealed class NotFoundExceptionHandle : IExceptionHandler
{
    private readonly ILogger<NotFoundExceptionHandle> _logger;

    public NotFoundExceptionHandle(ILogger<NotFoundExceptionHandle> logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        if (exception is not NotFoundException notFoundException)
        {
            return false;
        }

        _logger.LogError(
            notFoundException,
            "Exception: {Message}",
            notFoundException.Message);

        var problemDetails = new ProblemDetails
        {
            Status = StatusCodes.Status404NotFound,
            Title = "NotFound",
            Detail = notFoundException.Message
        };

        httpContext.Response.StatusCode = problemDetails.Status.Value;

        await httpContext.Response
            .WriteAsJsonAsync(problemDetails, cancellationToken);

        return true;
    }
}

您还需要通过调用来注册两个异常处理程序:AddExceptionHandler

builder.Services.AddExceptionHandler<BadRequestExceptionHandle>();  
builder.Services.AddExceptionHandler<NotFoundExceptionHandle>();

将首先执行并尝试处理异常。如果未处理异常,将执行下一步并尝试处理异常。

异常处理中的换档

虽然ASP.NET Core中间件在异常处理方面很好 ,但接口的引入提供了一种更加精简和灵活的方法。这种改进的系统允许集中管理和自定义处理逻辑,使 ASP.NET Core 8 项目中的错误管理更加高效和适应性强。

阅读排行