API过滤器

响应筛选器,用于统一格式化响应请求

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;


[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SkipApiResponseFilterAttribute : Attribute
{
}

public class ApiResponseActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // 在Action执行之前,你可以添加逻辑,但在这个场景中我们不需要。
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // 检查是否应该跳过ApiResponseActionFilter
        if (context.ActionDescriptor.EndpointMetadata.Any(em => em is SkipApiResponseFilterAttribute))
        {
            return; // 如果有SkipApiResponseFilterAttribute,则直接返回,不再处理。
        }

        object extraData = null;

        // 检查是否有额外的数据需要添加到响应中
        if (context.HttpContext.Items.ContainsKey("ExtraData"))
        {
            extraData = context.HttpContext.Items["ExtraData"];
        }

        //请求正常返回数据
        if (context.Result is ObjectResult objectResult)
        {
            var apiResponse = new CustomRestfulResult<object>()
            {
                StatusCode = objectResult.StatusCode??200,
                Data = objectResult.Value,
                Succeeded = true,
                Timestamp = DateTime.Now.GetTimeSpan(),
                Extras = extraData
            };
            context.Result = new ObjectResult(apiResponse)
            {
                StatusCode = objectResult.StatusCode
            };
        }
        //返回请求码
        else if (context.Result is StatusCodeResult statusCodeResult)
        {
            var apiResponse = new CustomRestfulResult<object>()
            {
                StatusCode = statusCodeResult.StatusCode,
                Data = null,
                Succeeded = false,
                Timestamp = DateTime.Now.GetTimeSpan(),
                Extras = extraData
            };
            context.Result = new ObjectResult(apiResponse)
            {
                StatusCode = statusCodeResult.StatusCode
            };
        }
    }

}

异常筛选器

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


public class ApiExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        object extraData = null;

        // 检查是否有额外的数据需要添加到响应中
        if (context.HttpContext.Items.ContainsKey("ExtraData"))
        {
            extraData = context.HttpContext.Items["ExtraData"];
        }

        // 你可以根据不同类型的异常返回不同的错误信息或状态码
        var statusCode = context.Exception is ArgumentException ?
            (int)HttpStatusCode.BadRequest :
            (int)HttpStatusCode.InternalServerError;

        var response = new CustomRestfulResult<string>
        {
            StatusCode = statusCode,
            Succeeded = false,
            Errors = context.Exception.Message,
            Timestamp = DateTime.Now.GetTimeSpan(),
            Extras = extraData
        };

        context.Result = new ObjectResult(response)
        {
            StatusCode = statusCode
        };

        // 设置为true,表示异常已经被处理
        context.ExceptionHandled = true; 
    }
}


/// <summary>
/// 客制化请求响应结果
/// </summary>
/// <typeparam name="T"></typeparam>
public class CustomRestfulResult<T>
{
    /// <summary>
    /// 状态码
    /// </summary>
    public int? StatusCode { get; set; }

    /// <summary>
    /// 数据
    /// </summary>
    public T? Data { get; set; }

    /// <summary>
    /// 执行成功
    /// </summary>
    public bool Succeeded { get; set; }

    /// <summary>
    /// 错误信息
    /// </summary>
    public object? Errors { get; set; }

    /// <summary>
    /// 附加数据
    /// </summary>
    public object? Extras { get; set; }

    /// <summary>
    /// 时间戳
    /// </summary>
    public long Timestamp { get; set; }
}

/// <summary>
/// 时间扩展
/// </summary>
public static class ExtensionForDateTime
{
    /// <summary>
    /// DateTime转换为Unix时间戳
    /// </summary>
    /// <param name="dateTime">当前时间</param>
    /// <returns>Unix时间戳(转换为秒)</returns>
    public static long GetTimeSpan(this DateTime dateTime)
    {
        // 转换为UTC时间
        DateTime utcNow = dateTime.ToUniversalTime();

        // Unix时间戳起点
        DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        // 计算时间差
        TimeSpan elapsed = utcNow.Subtract(unixEpoch);

        // 转换为秒(Unix时间戳)
        return (long)elapsed.TotalSeconds;
    }
}
using System.Globalization;
using System.Text.Json.Serialization;
using System.Text.Json;


/// <summary>
/// 自定义日期时间格式转换器
/// </summary>
public class JsonDateTimeConverter : JsonConverter<DateTime>
{
    private const string DateFormat = "yyyy-MM-dd HH:mm:ss"; // 自定义的日期时间格式

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTime.ParseExact(reader.GetString(), DateFormat, CultureInfo.InvariantCulture);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(DateFormat, CultureInfo.InvariantCulture));
    }
}

拓展

public static class ControllerExtensions
{
    public static IMvcBuilder AddCustomConfig(this IMvcBuilder builder)
    {
        // 在这里添加您的自定义配置
        builder.Services.AddControllers(options =>
        {
            // API 自动规范化RESTful风格
            options.Filters.Add(new ApiResponseActionFilter());
            // API 异常捕获
            options.Filters.Add(new ApiExceptionFilter());
            // API 日志记录 使用TypeFilter来实现依赖注入
            options.Filters.Add(typeof(ApiRequestResponseLoggingFilter));
        }).AddJsonOptions(options =>
        {
            options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter());
        });

        return builder;
    }
}

使用

builder.Services.AddControllers().AddCustomConfig();

api中使用

 [ApiController]
 [Route("[controller]")]
 public class RadarController : ControllerBase
 {
     /// <summary>
     /// hello
     /// </summary>
     /// <returns></returns>
     [HttpGet]
     [Route("Hello")]
     public string Hello()
     {
         return "hello";
     }
     
     /// <summary>
     /// GetCount
     /// </summary>
     /// <returns></returns>
     [HttpGet]
     [Route("GetCount")]
     public string GetCount()
     {
         //增加额外数据
         HttpContext.Items["ExtraData"] = new { totalCount = 100 };
         return "GetCount";
     }

     /// <summary>
     /// GetFile
     /// </summary>
     /// <returns></returns>
     [HttpGet]
     [Route("GetFile")]
     [SkipApiResponseFilterAttribute] //跳过格式化
     public IActionResult GetFile()
     {
        // 设置文件路径
        var filePath = Path.Combine(Directory.GetCurrentDirectory(), "Data", fileName);
        
        // 检查文件是否存在
        if (!System.IO.File.Exists(filePath))
        {
            return NotFound("文件不存在");
        }
        
        // 读取文件的byte数组
        byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
        
        // 返回文件流作为文件类型的响应
        return File(fileBytes, "application/octet-stream", fileName);
     }
 }