diff --git a/Learn.VideoAnalysis.API/Expand/HttpFilter.cs b/Learn.VideoAnalysis.API/Expand/HttpFilter.cs
new file mode 100644
index 0000000..ade2cd9
--- /dev/null
+++ b/Learn.VideoAnalysis.API/Expand/HttpFilter.cs
@@ -0,0 +1,279 @@
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using System;
+using System.Linq;
+using Microsoft.AspNetCore.Http;
+using SqlSugar;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.Text.Json;
+using System.Collections.Generic;
+using System.Data;
+using Microsoft.Extensions.Hosting;
+using System.IO;
+using Microsoft.AspNetCore.Hosting;
+using SqlSugar.IOC;
+using Microsoft.AspNetCore.Authorization;
+using VideoAnalysisCore.Common.Dto;
+using VideoAnalysisCore.Common;
+using VideoAnalysisCore.Model;
+
+namespace Learn.VideoAnalysis.API.Expand
+{
+
+ ///
+ /// 使用该属性,接口对结果原样输出,不做包装
+ ///
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public class ResultIgnore : Attribute { }
+
+ ///
+ /// http接口日志启用
+ ///
+ public class HttpLogEnable : Attribute { }
+
+ ///
+ /// Http请求过滤器
+ ///
+ public class HttpLogAttribute : ActionFilterAttribute, IAsyncExceptionFilter
+ {
+ readonly Stopwatch _stopwatch;//统计程序耗时
+
+ public HttpLogAttribute()
+ {
+ _stopwatch = Stopwatch.StartNew();
+ }
+
+ ///
+ /// 执行接口前文件做缓存处理
+ ///
+ ///
+ ///
+ public void ExecutingFileCached(ActionExecutingContext context)
+ {
+ //特殊处理:ResultIgnore,不进行返回结果包装,原样输出
+ var endpoint = context.HttpContext.GetEndpoint();
+ // 直接返回原始结果,不封装
+ if (endpoint?.Metadata.GetMetadata() == null) return;
+ if (context.HttpContext.Request.HasFormContentType &&
+ context.HttpContext.Request.Form.Files != null &&
+ context.HttpContext.Request.Form.Files.Count() > 0)
+ {
+ context.HttpContext.Items["FileCached"]=
+ context.HttpContext.Request.Form.Files.Select(s =>
+ {
+ var stream = new MemoryStream();
+ s.CopyTo(stream);
+ stream.Position = 0;
+ return (s, stream);
+ }).ToArray();
+ }
+
+ }
+ ///
+ /// 执行接口前400 处理
+ ///
+ ///
+ ///
+ public void Executing400(ActionExecutingContext context)
+ {
+ if (!context.ModelState.IsValid)
+ {
+ var errMsg = string.Join(',', context.ModelState.Values.SelectMany(s => s.Errors.Select(e => e.ErrorMessage)));
+ Oh.ModelError(errMsg);
+ }
+ }
+ private bool HasAttribute(ActionExecutedContext context) where T : Attribute
+ {
+ if (context.ActionDescriptor is ControllerActionDescriptor descriptor)
+ {
+ // 检查方法上是否有 SkipApiResultAttribute
+ if (descriptor.MethodInfo.GetCustomAttributes(typeof(T), false).Any())
+ return true;
+ // 检查控制器上是否有 SkipApiResultAttribute
+ if (descriptor.ControllerTypeInfo.GetCustomAttributes(typeof(T), false).Any())
+ return true;
+ }
+ return false;
+ }
+ ///
+ /// 接口结果格式化
+ ///
+ ///
+ public BaseReturn