.NET Core API 中的基础中间件

作者:微信公众号:【架构师老卢】
9-13 20:10
10

概述:在 Web 应用程序中处理异常可能很棘手,通常会导致未处理的异常和糟糕的用户体验。许多开发人员选择分散块等简单方法,这可能会导致不一致的错误处理和维护挑战。进入 ASP.NET Core 中不可或缺的功能 - 一个用于集中和强大错误处理的强大工具。在这篇博文中,我们将深入探讨在 ASP.NET Core 项目中使用此中间件的实现、集成和好处。您可以在以下 GitHub 存储库中找到完整的代码和项目结构:aspnet-core-global-exception-handler。try-catchGlobalExceptionHandlerMiddleware代码:概述以下是 our 及其集成到

在 Web 应用程序中处理异常可能很棘手,通常会导致未处理的异常和糟糕的用户体验。许多开发人员选择分散块等简单方法,这可能会导致不一致的错误处理和维护挑战。进入 ASP.NET Core 中不可或缺的功能 - 一个用于集中和强大错误处理的强大工具。在这篇博文中,我们将深入探讨在 ASP.NET Core 项目中使用此中间件的实现、集成和好处。您可以在以下 GitHub 存储库中找到完整的代码和项目结构:aspnet-core-global-exception-handler。try-catchGlobalExceptionHandlerMiddleware

代码:概述

以下是 our 及其集成到 ASP.NET Core 应用程序中的完整实现。我们将 Serilog 与内置功能一起使用,以利用 Serilog 的高级日志记录功能,例如记录到多个接收器(例如控制台、文件)和结构化日志记录。GlobalExceptionHandlerMiddlewareILogger

GlobalExceptionHandler中间件

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.Threading.Tasks;

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

    public GlobalExceptionHandlerMiddleware(RequestDelegate next, ILogger<GlobalExceptionHandlerMiddleware> logger)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An unhandled exception occurred while processing the request.");
            Log.Error(ex, "An unhandled exception occurred while processing the request.");

            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = "application/json";

            var response = new
            {
                Message = "An unexpected error occurred. Please try again later."
            };

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

Program.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;

var builder = WebApplication.CreateBuilder(args);

// Configure Serilog
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

builder.Host.UseSerilog();

builder.Services.AddProblemDetails();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/error");
}

app.UseMiddleware<GlobalExceptionHandlerMiddleware>();

app.MapGet("/", () => "Hello World!");

app.MapGet("/exception", () =>
{
    throw new InvalidOperationException("Sample Exception");
});

app.Run();

分解中间件

构造 函数

构造函数使用 next 和 .使用 of 可确保提供两个依赖项,从而防止潜在的 null 引用问题。RequestDelegateILoggerArgumentNullException

public GlobalExceptionHandlerMiddleware(RequestDelegate next, ILogger<GlobalExceptionHandlerMiddleware> logger)
{
    _next = next ?? throw new ArgumentNullException(nameof(next));
    _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

InvokeAsync 方法

这就是魔法发生的地方。该方法拦截请求并将其包装在一个块中。如果发生异常,它会同时使用 和 记录错误,然后使用 500 状态代码和 JSON 错误消息进行响应。InvokeAsynctry-catchILoggerSerilog

public async Task InvokeAsync(HttpContext context)
{
    try
    {
        await _next(context);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "An unhandled exception occurred while processing the request.");
        Log.Error(ex, "An unhandled exception occurred while processing the request.");

        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        context.Response.ContentType = "application/json";

        var response = new
        {
            Message = "An unexpected error occurred. Please try again later."
        };

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

为什么使用这个中间件?

使用此中间件有几个主要好处:

  1. 集中式错误处理:通过在一个位置捕获所有未处理的异常,您可以维护更清晰的控制器操作和更易维护的代码库。此方法可防止代码重复,并确保在整个应用程序中进行一致的错误处理。
  2. 增强的日志记录功能:同时使用两者并提供全面的日志记录功能。Serilog 能够记录到多个接收器(控制台、文件等)及其对结构化日志记录的支持,确保您不会错过重要信息。ILoggerSerilog
  3. 用户友好的响应:用户收到的不是通用的错误页面,而是标准化的 JSON 响应,从而改善了 API 的消费者体验并使其更容易调试问题。

稳健应用必不可少

一些开发人员认为,在整个代码中散布块就足够了。但是,这种方法通常会导致重复的代码和不一致的错误处理。例如,如果没有集中式错误处理,应用程序的不同部分可能会针对同一类型的异常返回不同的错误响应,从而导致混淆和维护麻烦。这提供了一种更简洁、更一致的方法。try-catchGlobalExceptionHandlerMiddleware

集成到 ASP.NET Core 应用程序中似乎是一个重大变化,但其好处远大于缺点。通过集中错误处理、增强日志记录和提供用户友好的响应,此中间件可确保您的应用程序健壮且可维护。立即开始在您的项目中实施它,以体验错误管理和应用程序稳定性的改进

阅读排行