diff --git a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/ExceptionMetadata.cs b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/ExceptionMetadata.cs
new file mode 100644
index 00000000..8277da2d
--- /dev/null
+++ b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/ExceptionMetadata.cs
@@ -0,0 +1,46 @@
+// MIT 许可证
+//
+// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
+//
+// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
+// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
+// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
+//
+// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
+//
+// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
+// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
+// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
+
+namespace Yi.Framework.AspNetCore.UnifyResult;
+
+///
+/// 异常元数据
+///
+public sealed class ExceptionMetadata
+{
+ ///
+ /// 状态码
+ ///
+ public int StatusCode { get; internal set; }
+
+ ///
+ /// 错误码
+ ///
+ public object ErrorCode { get; internal set; }
+
+ ///
+ /// 错误码(没被复写过的 ErrorCode )
+ ///
+ public object OriginErrorCode { get; internal set; }
+
+ ///
+ /// 错误对象(信息)
+ ///
+ public object Errors { get; internal set; }
+
+ ///
+ /// 额外数据
+ ///
+ public object Data { get; internal set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/FriendlyExceptionFilter.cs b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/FriendlyExceptionFilter.cs
new file mode 100644
index 00000000..cf86d283
--- /dev/null
+++ b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/FriendlyExceptionFilter.cs
@@ -0,0 +1,106 @@
+// MIT 许可证
+//
+// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
+//
+// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
+// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
+// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
+//
+// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
+//
+// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
+// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
+// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.DependencyInjection;
+using Yi.Framework.Core.Extensions;
+
+namespace Yi.Framework.AspNetCore.UnifyResult.Fiters;
+
+///
+/// 友好异常拦截器
+///
+public sealed class FriendlyExceptionFilter : IAsyncExceptionFilter
+{
+ ///
+ /// 异常拦截
+ ///
+ ///
+ ///
+ public async Task OnExceptionAsync(ExceptionContext context)
+ {
+
+ // 排除 WebSocket 请求处理
+ if (context.HttpContext.IsWebSocketRequest()) return;
+
+ // 如果异常在其他地方被标记了处理,那么这里不再处理
+ if (context.ExceptionHandled) return;
+
+ // 解析异常信息
+ var exceptionMetadata = GetExceptionMetadata(context);
+
+ IUnifyResultProvider unifyResult = context.GetRequiredService();
+ // 执行规范化异常处理
+ context.Result = unifyResult.OnException(context, exceptionMetadata);
+
+ // 创建日志记录器
+ var logger = context.HttpContext.RequestServices.GetRequiredService>();
+
+ // 记录拦截日常
+ logger.LogError(context.Exception, context.Exception.Message);
+ }
+
+ ///
+ /// 获取异常元数据
+ ///
+ ///
+ ///
+ public static ExceptionMetadata GetExceptionMetadata(ActionContext context)
+ {
+ object errorCode = default;
+ object originErrorCode = default;
+ object errors = default;
+ object data = default;
+ var statusCode = StatusCodes.Status500InternalServerError;
+ var isValidationException = false; // 判断是否是验证异常
+ var isFriendlyException = false;
+
+ // 判断是否是 ExceptionContext 或者 ActionExecutedContext
+ var exception = context is ExceptionContext exContext
+ ? exContext.Exception
+ : (
+ context is ActionExecutedContext edContext
+ ? edContext.Exception
+ : default
+ );
+
+ // 判断是否是友好异常
+ if (exception is UserFriendlyException friendlyException)
+ {
+ int statusCode2 = 500;
+ int.TryParse(friendlyException.Code, out statusCode2);
+ isFriendlyException = true;
+ errorCode = friendlyException.Code;
+ originErrorCode = friendlyException.Code;
+ statusCode = statusCode2==0?403:statusCode2;
+ isValidationException = false;
+ errors = friendlyException.Message;
+ data = friendlyException.Data;
+ }
+
+ return new ExceptionMetadata
+ {
+ StatusCode = statusCode,
+ ErrorCode = errorCode,
+ OriginErrorCode = originErrorCode,
+ Errors = errors,
+ Data = data
+ };
+ }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/SucceededUnifyResultFilter.cs b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/SucceededUnifyResultFilter.cs
new file mode 100644
index 00000000..62288f25
--- /dev/null
+++ b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Fiters/SucceededUnifyResultFilter.cs
@@ -0,0 +1,276 @@
+using System.Collections;
+using System.Reflection;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.DependencyInjection;
+using Yi.Framework.Core.Extensions;
+
+namespace Yi.Framework.AspNetCore.UnifyResult.Fiters;
+
+///
+/// 规范化结构(请求成功)过滤器
+///
+public class SucceededUnifyResultFilter : IAsyncActionFilter, IOrderedFilter
+{
+ ///
+ /// 过滤器排序
+ ///
+ private const int FilterOrder = 8888;
+
+ ///
+ /// 排序属性
+ ///
+ public int Order => FilterOrder;
+
+ ///
+ /// 处理规范化结果
+ ///
+ ///
+ ///
+ ///
+ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+ {
+ // 执行 Action 并获取结果
+ var actionExecutedContext = await next();
+
+ // 排除 WebSocket 请求处理
+ if (actionExecutedContext.HttpContext.IsWebSocketRequest()) return;
+
+ // 处理已经含有状态码结果的 Result
+ if (actionExecutedContext.Result is IStatusCodeActionResult statusCodeResult &&
+ statusCodeResult.StatusCode != null)
+ {
+ // 小于 200 或者 大于 299 都不是成功值,直接跳过
+ if (statusCodeResult.StatusCode.Value < 200 || statusCodeResult.StatusCode.Value > 299)
+ {
+ // 处理规范化结果
+ if (!CheckStatusCodeNonUnify(context.HttpContext, out var unifyRes))
+ {
+ var httpContext = context.HttpContext;
+ var statusCode = statusCodeResult.StatusCode.Value;
+
+ // 解决刷新 Token 时间和 Token 时间相近问题
+ if (statusCodeResult.StatusCode.Value == StatusCodes.Status401Unauthorized
+ && httpContext.Response.Headers.ContainsKey("access-token")
+ && httpContext.Response.Headers.ContainsKey("x-access-token"))
+ {
+ httpContext.Response.StatusCode = statusCode = StatusCodes.Status403Forbidden;
+ }
+
+ // 如果 Response 已经完成输出,则禁止写入
+ if (httpContext.Response.HasStarted) return;
+ await unifyRes.OnResponseStatusCodes(httpContext, statusCode,
+ httpContext.RequestServices.GetService>()?.Value);
+ }
+
+ return;
+ }
+ }
+
+ // 如果出现异常,则不会进入该过滤器
+ if (actionExecutedContext.Exception != null) return;
+
+ // 获取控制器信息
+ var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
+
+ // 判断是否支持 MVC 规范化处理,检测配置而已
+ // if (!UnifyContext.CheckSupportMvcController(context.HttpContext, actionDescriptor, out _)) return;
+
+ // 判断是否跳过规范化处理,检测NonUnifyAttribute而已
+ if (CheckSucceededNonUnify(actionDescriptor.MethodInfo))
+ {
+ return;
+ }
+ IUnifyResultProvider unifyResult = context.GetRequiredService();
+
+ // 处理 BadRequestObjectResult 类型规范化处理
+ if (actionExecutedContext.Result is BadRequestObjectResult badRequestObjectResult)
+ {
+ // 解析验证消息
+ var validationMetadata = GetValidationMetadata(badRequestObjectResult.Value);
+
+ var result = unifyResult.OnValidateFailed(context, validationMetadata);
+ if (result != null) actionExecutedContext.Result = result;
+ }
+ else
+ {
+ IActionResult result = default;
+
+ // 检查是否是有效的结果(可进行规范化的结果)
+ if (CheckVaildResult(actionExecutedContext.Result, out var data))
+ {
+ result = unifyResult.OnSucceeded(actionExecutedContext, data);
+ }
+
+ // 如果是不能规范化的结果类型,则跳过
+ if (result == null) return;
+
+ actionExecutedContext.Result = result;
+ }
+ }
+
+ ///
+ /// 获取验证错误信息
+ ///
+ ///
+ ///
+ private static ValidationMetadata GetValidationMetadata(object errors)
+ {
+ ModelStateDictionary _modelState = null;
+ object validationResults = null;
+ (string message, string firstErrorMessage, string firstErrorProperty) = (default, default, default);
+
+ // 判断是否是集合类型
+ if (errors is IEnumerable && errors is not string)
+ {
+ // 如果是模型验证字典类型
+ if (errors is ModelStateDictionary modelState)
+ {
+ _modelState = modelState;
+ // 将验证错误信息转换成字典并序列化成 Json
+ validationResults = modelState.Where(u => modelState[u.Key].ValidationState == ModelValidationState.Invalid)
+ .ToDictionary(u => u.Key, u => modelState[u.Key].Errors.Select(c => c.ErrorMessage).ToArray());
+ }
+ // 如果是 ValidationProblemDetails 特殊类型
+ else if (errors is ValidationProblemDetails validation)
+ {
+ validationResults = validation.Errors
+ .ToDictionary(u => u.Key, u => u.Value.ToArray());
+ }
+ // 如果是字典类型
+ else if (errors is Dictionary dicResults)
+ {
+ validationResults = dicResults;
+ }
+
+ message = JsonSerializer.Serialize(validationResults, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = true
+ });
+ firstErrorMessage = (validationResults as Dictionary).First().Value[0];
+ firstErrorProperty = (validationResults as Dictionary).First().Key;
+ }
+ // 其他类型
+ else
+ {
+ validationResults = firstErrorMessage = message = errors?.ToString();
+ }
+
+ return new ValidationMetadata
+ {
+ ValidationResult = validationResults,
+ Message = message,
+ ModelState = _modelState,
+ FirstErrorProperty = firstErrorProperty,
+ FirstErrorMessage = firstErrorMessage
+ };
+ }
+
+ ///
+ /// 检查是否是有效的结果(可进行规范化的结果)
+ ///
+ ///
+ ///
+ ///
+ private bool CheckVaildResult(IActionResult result, out object data)
+ {
+ data = default;
+
+ // 排除以下结果,跳过规范化处理
+ var isDataResult = result switch
+ {
+ ViewResult => false,
+ PartialViewResult => false,
+ FileResult => false,
+ ChallengeResult => false,
+ SignInResult => false,
+ SignOutResult => false,
+ RedirectToPageResult => false,
+ RedirectToRouteResult => false,
+ RedirectResult => false,
+ RedirectToActionResult => false,
+ LocalRedirectResult => false,
+ ForbidResult => false,
+ ViewComponentResult => false,
+ PageResult => false,
+ NotFoundResult => false,
+ NotFoundObjectResult => false,
+ _ => true,
+ };
+
+ // 目前支持返回值 ActionResult
+ if (isDataResult) data = result switch
+ {
+ // 处理内容结果
+ ContentResult content => content.Content,
+ // 处理对象结果
+ ObjectResult obj => obj.Value,
+ // 处理 JSON 对象
+ JsonResult json => json.Value,
+ _ => null,
+ };
+
+ return isDataResult;
+ }
+
+
+ ///
+ /// 检查短路状态码(>=400)是否进行规范化处理
+ ///
+ ///
+ ///
+ /// 返回 true 跳过处理,否则进行规范化处理
+ internal static bool CheckStatusCodeNonUnify(HttpContext context, out IUnifyResultProvider unifyResult)
+ {
+ // 获取终点路由特性
+ var endpointFeature = context.Features.Get();
+ if (endpointFeature == null) return (unifyResult = null) == null;
+
+ // 判断是否跳过规范化处理
+ var isSkip = context.GetEndpoint()?.Metadata?.GetMetadata()!= null
+ || endpointFeature?.Endpoint?.Metadata?.GetMetadata() != null
+ || context.Request.Headers["accept"].ToString().Contains("odata.metadata=", StringComparison.OrdinalIgnoreCase)
+ || context.Request.Headers["accept"].ToString().Contains("odata.streaming=", StringComparison.OrdinalIgnoreCase);
+
+ if (isSkip == true) unifyResult = null;
+ else
+ {
+ unifyResult = context.RequestServices.GetRequiredService();
+ }
+
+ return unifyResult == null || isSkip;
+ }
+
+ ///
+ /// 检查请求成功是否进行规范化处理
+ ///
+ ///
+ ///
+ /// 返回 true 跳过处理,否则进行规范化处理
+ private bool CheckSucceededNonUnify(MethodInfo method, bool isWebRequest = true)
+ {
+ // 判断是否跳过规范化处理
+ var isSkip = method.CustomAttributes.Any(x => typeof(NonUnifyAttribute).IsAssignableFrom(x.AttributeType) || typeof(ProducesResponseTypeAttribute).IsAssignableFrom(x.AttributeType) || typeof(IApiResponseMetadataProvider).IsAssignableFrom(x.AttributeType))
+ || method.ReflectedType.IsDefined(typeof(NonUnifyAttribute), true)
+ || method.DeclaringType.Assembly.GetName().Name.StartsWith("Microsoft.AspNetCore.OData");
+
+ if (!isWebRequest)
+ {
+ return isSkip;
+ }
+ return isSkip;
+ }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/IUnifyResultProvider.cs b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/IUnifyResultProvider.cs
new file mode 100644
index 00000000..f8ac41eb
--- /dev/null
+++ b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/IUnifyResultProvider.cs
@@ -0,0 +1,58 @@
+// MIT 许可证
+//
+// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
+//
+// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
+// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
+// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
+//
+// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
+//
+// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
+// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
+// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace Yi.Framework.AspNetCore.UnifyResult;
+
+///
+/// 规范化结果提供器
+///
+public interface IUnifyResultProvider
+{
+ ///
+ /// 异常返回值
+ ///
+ ///
+ ///
+ ///
+ IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata);
+
+ ///
+ /// 成功返回值
+ ///
+ ///
+ ///
+ ///
+ IActionResult OnSucceeded(ActionExecutedContext context, object data);
+
+ ///
+ /// 验证失败返回值
+ ///
+ ///
+ ///
+ ///
+ IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata);
+
+ ///
+ /// 拦截返回状态码
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings = default);
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/NonUnifyAttribute.cs b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/NonUnifyAttribute.cs
new file mode 100644
index 00000000..fee1e9f8
--- /dev/null
+++ b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/NonUnifyAttribute.cs
@@ -0,0 +1,23 @@
+// MIT 许可证
+//
+// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
+//
+// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
+// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
+// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
+//
+// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
+//
+// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
+// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
+// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
+
+namespace Yi.Framework.AspNetCore.UnifyResult;
+
+///
+/// 禁止规范化处理
+///
+[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
+public sealed class NonUnifyAttribute : Attribute
+{
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Providers/RESTfulResultProvider.cs b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Providers/RESTfulResultProvider.cs
new file mode 100644
index 00000000..033b8a8c
--- /dev/null
+++ b/Yi.Abp.Net8/framework/Yi.Framework.AspNetCore/UnifyResult/Providers/RESTfulResultProvider.cs
@@ -0,0 +1,136 @@
+// MIT 许可证
+//
+// 版权 © 2020-present 百小僧, 百签科技(广东)有限公司 和所有贡献者
+//
+// 特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,
+// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,
+// 以及再授权被配发了本软件的人如上的权利,须在下列条件下:
+//
+// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
+//
+// 本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。
+// 在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,
+// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Volo.Abp.DependencyInjection;
+
+namespace Yi.Framework.AspNetCore.UnifyResult.Providers;
+
+///
+/// RESTful 风格返回值
+///
+[Dependency(TryRegister = true)]
+[ExposeServices(typeof(IUnifyResultProvider))]
+public class RESTfulResultProvider : IUnifyResultProvider,ITransientDependency
+{
+ ///
+ /// 设置响应状态码
+ ///
+ ///
+ ///
+ ///
+ public static void SetResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
+ {
+ if (unifyResultSettings == null) return;
+
+ // 篡改响应状态码
+ if (unifyResultSettings.AdaptStatusCodes != null && unifyResultSettings.AdaptStatusCodes.Length > 0)
+ {
+ var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode);
+ if (adaptStatusCode != null && adaptStatusCode.Length > 0 && adaptStatusCode[0] > 0)
+ {
+ context.Response.StatusCode = adaptStatusCode[1];
+ return;
+ }
+ }
+
+ // 如果为 null,则所有请求错误的状态码设置为 200
+ if (unifyResultSettings.Return200StatusCodes == null) context.Response.StatusCode = 200;
+ // 否则只有里面的才设置为 200
+ else if (unifyResultSettings.Return200StatusCodes.Contains(statusCode)) context.Response.StatusCode = 200;
+ else { }
+ }
+
+ ///
+ /// 异常返回值
+ ///
+ ///
+ ///
+ ///
+ public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
+ {
+ return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors));
+ }
+
+ ///
+ /// 成功返回值
+ ///
+ ///
+ ///
+ ///
+ public IActionResult OnSucceeded(ActionExecutedContext context, object data)
+ {
+ return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
+ }
+
+ ///
+ /// 验证失败/业务异常返回值
+ ///
+ ///
+ ///
+ ///
+ public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
+ {
+ return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult));
+ }
+
+ ///
+ /// 特定状态码返回值
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
+ {
+ // 设置响应状态码
+ SetResponseStatusCodes(context, statusCode, unifyResultSettings);
+
+ switch (statusCode)
+ {
+ // 处理 401 状态码
+ case StatusCodes.Status401Unauthorized:
+ await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 Unauthorized"));
+ break;
+ // 处理 403 状态码
+ case StatusCodes.Status403Forbidden:
+ await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 Forbidden"));
+ break;
+
+ default: break;
+ }
+ }
+
+ ///
+ /// 返回 RESTful 风格结果集
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static RESTfulResult