using NCC.Dependency; using NCC.Extensions; using NCC.FriendlyException; using NCC.Templates.Extensions; using NCC.UnifyResult.Internal; 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.RazorPages; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using System; using System.Linq; using System.Reflection; namespace NCC.UnifyResult { /// /// 规范化结果上下文 /// [SuppressSniffer] public static class UnifyContext { /// /// 是否启用规范化结果 /// internal static bool EnabledUnifyHandler = false; /// /// 规范化结果类型 /// internal static Type RESTfulResultType = typeof(RESTfulResult<>); /// /// 规范化结果额外数据键 /// internal static string UnifyResultExtrasKey = "UNIFY_RESULT_EXTRAS"; /// /// 获取异常元数据 /// /// /// public static ExceptionMetadata GetExceptionMetadata(ExceptionContext context) { object errorCode = default; object errors = default; var statusCode = StatusCodes.Status500InternalServerError; var isValidationException = false; // 判断是否是验证异常 var isFriendlyException = false; // 判断是否是友好异常 if (context.Exception is AppFriendlyException friendlyException) { isFriendlyException = true; errorCode = friendlyException.ErrorCode; statusCode = friendlyException.StatusCode; isValidationException = friendlyException.ValidationException; errors = friendlyException.ErrorMessage; } // 处理验证失败异常 if (!isValidationException) { // 判断是否定义了全局类型异常 var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; // 查找所有全局定义异常 var ifExceptionAttributes = actionDescriptor.MethodInfo .GetCustomAttributes(true) .Where(u => u.ErrorCode == null); // 处理全局异常 if (ifExceptionAttributes.Any()) { // 首先判断是否有相同类型的异常 var actionIfExceptionAttribute = ifExceptionAttributes.FirstOrDefault(u => u.ExceptionType == (isFriendlyException && context.Exception.InnerException != null ? context.Exception.InnerException.GetType() : context.Exception.GetType())) ?? ifExceptionAttributes.FirstOrDefault(u => u.ExceptionType == null); // 支持渲染配置文件 if (actionIfExceptionAttribute is { ErrorMessage: not null }) errors = actionIfExceptionAttribute.ErrorMessage.Render(); } else errors = context.Exception?.InnerException?.Message ?? context.Exception.Message; } return new ExceptionMetadata { StatusCode = statusCode, ErrorCode = errorCode, Errors = errors }; } /// /// 填充附加信息 /// /// public static void Fill(object extras) { var items = App.HttpContext?.Items; if (items.ContainsKey(UnifyResultExtrasKey)) items.Remove(UnifyResultExtrasKey); items.Add(UnifyResultExtrasKey, extras); } /// /// 读取附加信息 /// public static object Take() { object extras = null; App.HttpContext?.Items?.TryGetValue(UnifyResultExtrasKey, out extras); return extras; } /// /// 设置响应状态码 /// /// /// /// public static void SetResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings) { // 篡改响应状态码 if (unifyResultSettings.AdaptStatusCodes != null && unifyResultSettings.AdaptStatusCodes.Length > 0) { var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode); if (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 { } } /// /// 检查请求成功是否进行规范化处理 /// /// /// /// /// 返回 true 跳过处理,否则进行规范化处理 internal static bool CheckSucceededNonUnify(MethodInfo method, out IUnifyResultProvider unifyResult, bool isWebRequest = true) { // 判断是否跳过规范化处理 var isSkip = !EnabledUnifyHandler || method.GetRealReturnType().HasImplementedRawGeneric(RESTfulResultType) || 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); if (!isWebRequest) { unifyResult = null; return isSkip; } unifyResult = isSkip ? null : App.RootServices.GetService(); return unifyResult == null || isSkip; } /// /// 检查请求失败(验证失败、抛异常)是否进行规范化处理 /// /// /// /// 返回 true 跳过处理,否则进行规范化处理 internal static bool CheckFailedNonUnify(MethodInfo method, out IUnifyResultProvider unifyResult) { // 判断是否跳过规范化处理 var isSkip = !EnabledUnifyHandler || method.CustomAttributes.Any(x => typeof(NonUnifyAttribute).IsAssignableFrom(x.AttributeType)) || ( !method.CustomAttributes.Any(x => typeof(ProducesResponseTypeAttribute).IsAssignableFrom(x.AttributeType) || typeof(IApiResponseMetadataProvider).IsAssignableFrom(x.AttributeType)) && method.ReflectedType.IsDefined(typeof(NonUnifyAttribute), true) ); unifyResult = isSkip ? null : App.RootServices.GetService(); return unifyResult == null || isSkip; } /// /// 检查短路状态码(>=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 = !EnabledUnifyHandler || context.GetMetadata() != null || endpointFeature?.Endpoint?.Metadata?.GetMetadata() != null; unifyResult = isSkip ? null : context.RequestServices.GetService(); return unifyResult == null || isSkip; } /// /// 判断是否支持 Mvc 控制器规范化处理 /// /// /// /// /// internal static bool CheckSupportMvcController(HttpContext httpContext, ControllerActionDescriptor actionDescriptor, out UnifyResultSettingsOptions unifyResultSettings) { // 获取规范化配置选项 unifyResultSettings = httpContext.RequestServices.GetService>()?.Value; // 如果未启用 MVC 规范化处理,则跳过 if (unifyResultSettings?.SupportMvcController == false && typeof(Controller).IsAssignableFrom(actionDescriptor.ControllerTypeInfo)) return false; return true; } /// /// 检查是否是有效的结果(可进行规范化的结果) /// /// /// /// internal static 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, _ => true, }; // 目前支持返回值 ActionResult if (isDataResult) data = result switch { // 处理内容结果 ContentResult content => content.Content, // 处理对象结果 ObjectResult obj => obj.Value, _ => null }; return isDataResult; } } }