【.NET 8 Web API】简单的异常处理 Filter、最终一致性事务 Filter 的实现

FIlter 一般存放在 Web API 层的 Filters 目录下,以 Filter 为后缀命名。

异常处理 Filter

使用异常 Filter 后,不必在业务代码中增加繁多的 try-catch 块,统一在全局的异常处理 Filter 内处理异常。

ApiResponse

namespace Model.DTO
{
    public class ApiResponse
    {
        public object? Data { get; set; }
        public string? Message { get; set; }
    }
}

ExceptionHandlingFilter

using Model.DTO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Net;

namespace CampusServicePlatform.WebAPI.Filters
{
    public class ExceptionHandlingFilter : IAsyncExceptionFilter
    {
        private readonly ILogger<ExceptionHandlingFilter> _logger;
        private readonly IHostEnvironment _hostEnvironment;

        public ExceptionHandlingFilter(ILogger<ExceptionHandlingFilter> logger, IHostEnvironment hostEnvironment)
        {
            _logger = logger;
            _hostEnvironment = hostEnvironment;
        }

        public Task OnExceptionAsync(ExceptionContext context)
        {
            Exception exception = context.Exception;

            _logger.LogError(exception, exception.Message);

            ApiResponse response = new ApiResponse();

            if (_hostEnvironment.IsDevelopment())
            {
                response.Message = exception.Message;
            }
            else
            {
                response.Message = "服务器异常,请稍后重试";
            }

            ObjectResult result = new ObjectResult(response);

            int statusCode = -1;
            switch (exception)
            {
                case ApplicationException:
                    {
                        if (exception.Message.Contains("Invalid token"))
                        {
                            statusCode = (int)HttpStatusCode.Forbidden;
                            break;
                        }
                        statusCode = (int)HttpStatusCode.BadRequest;
                        break;
                    }
                case KeyNotFoundException:
                    {
                        statusCode = (int)HttpStatusCode.NotFound;
                        break;
                    }
                default:
                    {
                        statusCode = (int)HttpStatusCode.InternalServerError;
                        break;
                    }
            }
            result.StatusCode = statusCode;

            context.Result = result;
            context.ExceptionHandled = true;

            return Task.CompletedTask;
        }
    }
}

最终一致性事务 Filter

可以使用操作 Filter 实现事务的最终一致性提交,最常应用在一个业务方法内,其中一个表的数据可以正常提交,另一个与之关联的表因为某些原因,没有按照预期提交两个相关联的表到数据库的情况,例如:

_context.Users.Add(new User());
throw new Exception();
_context.UserInfos.Add(new UserInfo());

TransactionalAttribute

一般来说,不是所有 API 方法都需要用到数据库操作,这里定义一个 Attribute 来标记需要使用到事务的 API 方法。

namespace Model.Attributes
{
    [AttributeUsage(AttributeTargets.Method)]
    public class TransactionalAttribute : Attribute
    {
    }
}

TransactionScopeFilter

using Model.Attributes;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Transactions;

namespace WebAPI.Filters
{
    public class TransactionScopeFilter : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            bool hasTransactionalAttribute = false;
            if (context.ActionDescriptor is ControllerActionDescriptor)
            {
                var actionDescriptor = (ControllerActionDescriptor)context.ActionDescriptor;
                hasTransactionalAttribute = actionDescriptor.MethodInfo.IsDefined(typeof(TransactionalAttribute), false);
            }

            if (!hasTransactionalAttribute)
            {
                await next();
                return;
            }

            using (TransactionScope transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                var actionExecutedContext = await next();

                if(actionExecutedContext.Exception != null)
                {
                    return;
                }

                transaction.Complete();
            }
        }
    }
}

注册 Filter

需要注意的是,Filter 的注册顺序是会影响运行结果的。

例如,在操作 Filter 中,API 方法执行前,会先进入注册在前的 Filter;API 方法执行后,会先进入注册在后的 Filter。

var builder = WebApplication.CreateBuilder(args);
// ...
builder.Services.AddControllers(options =>
{
    options.Filters.Add<TransactionScopeFilter>();
    options.Filters.Add<ExceptionHandlingFilter>();
});
// ...
var app = builder.Build();

参考资料

[1] 杨中科. ASP.NET Core技术内幕与项目实战:基于DDD与前后端分离[M]. 北京: 人民邮电出版社, 2022.

[2] 杨中科. .NET 6教程,.Net Core 2022视频教程,杨中科主讲[Z/OL]. https://www.bilibili.com/video/BV1pK41137He?p=134. 2020.

[3] 董川民. .NET6中全局异常处理[EB/OL]. https://www.dongchuanmin.com/net/1600.html. 2022.

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值