亲手搓一个 ASP.NET Core 过滤器使用 统一返回值模型/规范化结果/API 返回值

本文介绍了如何在ASP.NETCore.Net7.0项目中创建和使用过滤器来实现API的统一返回值模型,包括验证过滤器、异常过滤器和操作过滤器。通过自定义过滤器,可以标准化错误和成功响应,提供更好的API交互体验。同时,文章探讨了解决验证和操作拦截器可能出现的问题,以及提供了两种解决方案。
摘要由CSDN通过智能技术生成

亲手搓一个 ASP.NET Core 过滤器使用 统一返回值模型/规范化结果/API 返回值

环境 .Net 7.0 项目 ASP.NET Core WebAPI

筛选器、过滤器、拦截器介绍

ASP.NET Core 中的筛选器

请求管道

创建统一返回值基类

public class BaseResultModel
{
    public int? Code { get; set; }

    public string Message { get; set; }

    public object? Data { get; set; }

    public int ReturnStatus { get; set; }
}

拦截特性验证过滤器

IActionFilter 接口

编号方法说明
1OnActionExecuted(ActionExecutedContext)在操作执行后调用,然后执行操作结果。
2OnActionExecuting(ActionExecutingContext)在操作执行之前调用,模型绑定完成后。

自定义验证过滤器

public class ValidationError
{
    public ValidationError(string field, string message)
    {
        Field = field;
        Message = message;
    }

    public string Field { get; set; }

    public string Message { get; set; }
}
/// <summary>
/// 验证过滤器
/// </summary>
public class MyValidationFilterAttribute : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid) // 是否通过验证
        {
            var result = context.ModelState.Keys
                .SelectMany(key => context.ModelState[key]!.Errors.Select(x => new ValidationError(key, x.ErrorMessage)))
                .ToList();
            context.Result = new ObjectResult(new BaseResultModel
            {
                Code = 400,
                Message = "error",
                Data = result,
                ReturnStatus = 0
            });
        }
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // throw new NotImplementedException();
    }
}

添加过滤器

添加过滤器

解除默认设置

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
});

创建业务

// Person类用于接口请求数据
public class Person
{
    [Range(1,10,ErrorMessage = "范围1-10")]
    public int Age { get; set; }

    [MinLength(2,ErrorMessage = "名称不能低于2个字符")]
    public string Name { get; set; } = String.Empty;
}

// API接口
[HttpPost]
public Person AddPerson(Person input)
{
    return input;
}

事例

未通过特性验证

验证失败事例

验证成功事例

拦截异常过滤器

IExceptionFilter 接口

编号方法说明
1OnException(ExceptionContext)在操作引发后 Exception调用

自定义异常过滤器

/// <summary>
/// 异常过滤器
/// </summary>
public class MyExceptionFilterAttribute : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        var exception = context.Exception;
        context.ExceptionHandled = true;
        context.Result = new ObjectResult(new BaseResultModel()
        {
            Code = 400,
            Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message,
            Data = exception.Message, // 可改为其他值
            ReturnStatus = 3
        });
    }
}

创建业务

[HttpGet]
public void ThrowException()
{
    throw new Exception("抛出了一个异常");
}

添加过滤器

添加异常拦截过滤器

事例

拦截异常

验证异常拦截

操作筛选器

IActionFilter 接口

编号方法说明
1OnActionExecuted(ActionExecutedContext)在操作执行后调用,然后执行操作结果。
2OnActionExecuting(ActionExecutingContext)在操作执行之前调用,模型绑定完成后。

自定义操作筛选器

/// <summary>
/// 操作拦截器
/// </summary>
public class MyActionFilterAttribute : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // throw new NotImplementedException();
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        var result = context.Result as ObjectResult;
        context.Result = new OkObjectResult(new BaseResultModel
        {
            Code = 200,
            Message = "success",
            Data = result?.Value,
            ReturnStatus = 1
        });
    }
}

创建业务

[HttpPost]
public DateTime GetNowTime()
{
    return DateTime.Now;
}

添加过滤器

添加过滤器

事例

请求成功

使用拦截器统一返回值,将三个过滤器添加到配置中

builder.Services.AddMvc(options =>
{
    options.Filters.Add<MyActionFilterAttribute>();
    options.Filters.Add<MyExceptionFilterAttribute>();
    options.Filters.Add<MyValidationFilterAttribute>();
});

优化

原因

由于【特性拦截器】和【操作拦截器】都继承了IActionFilter接口,但拦截位置不同所以会出现异常信息包含在操作拦截器内(套娃现象),所以要修改两个拦截器内逻辑

套娃

解决一

取消特性验证拦截器,在操作拦截器内进行验证判断

/// <summary>
/// 操作拦截器
/// </summary>
public class MyActionFilterAttribute : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // throw new NotImplementedException();
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (!context.ModelState.IsValid)
        {
            var result = context.ModelState.Keys
                .SelectMany(key => context.ModelState[key]!.Errors.Select(x => new ValidationError(key, x.ErrorMessage)))
                .ToList();
            context.Result = new ObjectResult(new BaseResultModel
            {
                Code = 400,
                Message = "error",
                Data = result,
                ReturnStatus = ReturnStatus.Fail
            });
        }
        else
        {
            var statusCode = HttpStatusCode.OK;
            var result = context.Result as ObjectResult;
            context.Result = new OkObjectResult(new BaseResultModel
            {
                Code = (int)statusCode,
                Message = "success",
                Data = result?.Value,
                ReturnStatus = ReturnStatus.Success
            });
        }
    }
}

解决二

/// <summary>
/// 操作拦截器
/// </summary>
public class MyActionFilterAttribute : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            var result = context.ModelState.Keys
                .SelectMany(
                    key => context.ModelState[key]!.Errors.Select(x => new ValidationError(key, x.ErrorMessage)))
                .ToList();
            context.Result = new ObjectResult(new BaseResultModel
            {
                Code = 400,
                Message = "error",
                Data = result,
                ReturnStatus = ReturnStatus.Fail
            });
        }
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        var statusCode = HttpStatusCode.OK;
        var result = context.Result as ObjectResult;
        context.Result = new OkObjectResult(new BaseResultModel
        {
            Code = (int)statusCode,
            Message = "success",
            Data = result?.Value,
            ReturnStatus = ReturnStatus.Success
        });
    }
}

完整代码

/// <summary>
/// 异常过滤器
/// </summary>
public class MyExceptionFilterAttribute : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        var exception = context.Exception;
        context.ExceptionHandled = true;
        context.Result = new ObjectResult(new BaseResultModel()
        {
            Code = 400,
            Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message,
            Data = exception.Message,
            ReturnStatus = ReturnStatus.Error
        });
    }
}

/// <summary>
/// 操作拦截器/特性拦截器
/// </summary>
public class MyActionFilterAttribute : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            var result = context.ModelState.Keys
                .SelectMany(
                    key => context.ModelState[key]!.Errors.Select(x => new ValidationError(key, x.ErrorMessage)))
                .ToList();
            context.Result = new ObjectResult(new BaseResultModel
            {
                Code = 400,
                Message = "error",
                Data = result,
                ReturnStatus = ReturnStatus.Fail
            });
        }
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        var statusCode = HttpStatusCode.OK;
        var result = context.Result as ObjectResult;
        context.Result = new OkObjectResult(new BaseResultModel
        {
            Code = (int)statusCode,
            Message = "success",
            Data = result?.Value,
            ReturnStatus = ReturnStatus.Success
        });
    }
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值