ZetCode

ASP.NET ExceptionFilter

最后修改于 2025 年 4 月 3 日

在本文中,我们将探讨 ASP.NET 8 中的异常过滤器。异常过滤器提供了一种集中且一致的方式来处理异常。

ASP.NET 是一个跨平台、高性能的框架,用于构建现代 Web 应用程序。异常过滤器有助于跨控制器管理错误处理。

基本定义

ASP.NET 中的异常过滤器是实现 IExceptionFilter 或 IAsyncExceptionFilter 接口的特性。它们会捕获控制器中未处理的异常。

当异常发生在操作执行期间时,异常过滤器可以在异常到达客户端之前拦截它。它们允许自定义错误响应和日志记录。

异常过滤器可以在不同级别应用:全局、控制器范围或特定操作。它们比传统的 try-catch 块提供了更多的控制。

ASP.NET ExceptionFilter 示例

以下示例演示了如何创建和使用自定义异常过滤器。

Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.Filters.Add<CustomExceptionFilter>();
});

var app = builder.Build();

app.MapControllers();
app.Run();

这会将我们的 CustomExceptionFilter 全局注册到所有控制器。该过滤器将处理应用程序中的所有未处理异常。

Filters/CustomExceptionFilter.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Net;

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly ILogger<CustomExceptionFilter> _logger;

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

    public void OnException(ExceptionContext context)
    {
        _logger.LogError(context.Exception, 
            "An unhandled exception occurred");

        var problemDetails = new ProblemDetails
        {
            Title = "An error occurred",
            Status = (int)HttpStatusCode.InternalServerError,
            Detail = context.Exception.Message,
            Instance = context.HttpContext.Request.Path
        };

        if (context.Exception is ArgumentException)
        {
            problemDetails.Status = (int)HttpStatusCode.BadRequest;
            problemDetails.Title = "Invalid argument";
        }
        else if (context.Exception is KeyNotFoundException)
        {
            problemDetails.Status = (int)HttpStatusCode.NotFound;
            problemDetails.Title = "Resource not found";
        }

        context.Result = new ObjectResult(problemDetails)
        {
            StatusCode = problemDetails.Status
        };

        context.ExceptionHandled = true;
    }
}

此过滤器记录异常并将它们转换为标准化的 ProblemDetails 响应。不同的异常类型会导致不同的 HTTP 状态码。

Controllers/ProductsController.cs
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private static List<Product> _products = new()
    {
        new Product(1, "Laptop", 999.99m),
        new Product(2, "Mouse", 19.99m),
        new Product(3, "Keyboard", 49.99m)
    };

    [HttpGet("{id}")]
    public IActionResult GetProductById(int id)
    {
        var product = _products.FirstOrDefault(p => p.Id == id);
        if (product == null)
        {
            throw new KeyNotFoundException($"Product with ID {id} not found");
        }
        return Ok(product);
    }

    [HttpPost]
    public IActionResult CreateProduct([FromBody] Product product)
    {
        if (product.Price <= 0)
        {
            throw new ArgumentException("Price must be greater than zero");
        }
        _products.Add(product);
        return CreatedAtAction(nameof(GetProductById), 
            new { id = product.Id }, product);
    }
}

public record Product(int Id, string Name, decimal Price);

控制器会抛出会被我们的过滤器捕获的异常。该过滤器会将这些异常转换为带有 ProblemDetails 的适当 HTTP 响应。

当抛出 KeyNotFoundException 时,过滤器返回 404 状态。对于 ArgumentException,它返回 400。所有其他异常都导致 500 响应。

这种方法集中了错误处理逻辑,同时保持了控制器的代码整洁。该过滤器确保了整个 API 的一致错误响应。

来源

Microsoft ASP.NET 异常过滤器文档

在本文中,我们探讨了 ASP.NET 8 中的异常过滤器。这些强大的过滤器有助于创建具有一致错误处理模式的健壮 API。

作者

我的名字是 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 ASP.NET 教程