diff --git a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs index db01b70..fa21b59 100644 --- a/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs +++ b/src/CloudGaming/Api/CloudGaming.Api/Controllers/AppController.cs @@ -1,4 +1,7 @@ using CloudGaming.Api.Base; +using CloudGaming.GameModel.Db.Db_Ext; + +using HuanMeng.DotNetCore.Base; using Microsoft.AspNetCore.Mvc; @@ -19,9 +22,19 @@ namespace CloudGaming.Api.Controllers /// /// [HttpGet] - public async Task GetConfig() + public async Task GetConfig() { - return "a"; + return new { test = "测试", Data = new { txt = "内部数据" } }; + } + + /// + /// + /// + /// + [HttpGet] + public async Task> GetConfig1() + { + return new HuanMeng.DotNetCore.Base.BaseResponse(ResonseCode.Success, "成功", new T_App_Config() { BossId = "aa" }); } } } diff --git a/src/CloudGaming/Api/CloudGaming.Api/Program.cs b/src/CloudGaming/Api/CloudGaming.Api/Program.cs index 98134d2..2a2ff27 100644 --- a/src/CloudGaming/Api/CloudGaming.Api/Program.cs +++ b/src/CloudGaming/Api/CloudGaming.Api/Program.cs @@ -11,6 +11,11 @@ using Microsoft.OpenApi.Models; using Newtonsoft.Json.Serialization; using Microsoft.AspNetCore.Authentication.JwtBearer; using Serilog; +using Newtonsoft.Json; +using CloudGaming.Code.AppExtend.JsonConverHelper.JsonConverterUtil; +using CloudGaming.Code.AppExtend.JsonConverHelper; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.Extensions.Options; var builder = WebApplication.CreateBuilder(args); #region 日志 // Add services to the container. @@ -31,21 +36,36 @@ builder.Services.AddSingleton(typeof(ILogger), serviceProvi return loggerFactory.CreateLogger(); }); #endregion - -#region 返回数据解析 -builder.Services.AddControllers() +builder.Services.AddHttpClient(); +builder.Services.AddHttpContextAccessor(); //添加httpContext注入访问 +#region 返回数据解析 +//services.AddControllers(options => +//{ +// // 添加自定义的 ResultFilter 到全局过滤器中 +// options.Filters.Add(); +//}); +builder.Services.AddControllers(options => +{ + // 添加自定义的 ResultFilter 到全局过滤器中 + options.Filters.Add(); +}) .AddNewtonsoftJson(options => { // 配置 Newtonsoft.Json 选项 options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; // 忽略循环引用 options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();// 首字母小写(驼峰样式) + //options.SerializerSettings.ContractResolver = new LanguageContractResolver(builder.Services); options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";// 时间格式化 + options.SerializerSettings.Converters.Add(new StringConverter()); + + //options.SerializerSettings.ContractResolver = #if !DEBUG options.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.None; #endif //options.SerializerSettings.Converters.Add() // 其他配置... }); +builder.Services.AddSingleton(); #endregion // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomObjectResultExecutor.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomObjectResultExecutor.cs new file mode 100644 index 0000000..45d4d7d --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomObjectResultExecutor.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Org.BouncyCastle.Asn1.Ocsp; + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.Code.AppExtend +{ + public class CustomObjectResultExecutor : ObjectResultExecutor + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public CustomObjectResultExecutor(OutputFormatterSelector formatterSelector, IHttpResponseStreamWriterFactory writerFactory, ILoggerFactory loggerFactory, IOptions mvcOptions):base(formatterSelector, writerFactory, loggerFactory, mvcOptions) + + { + //_httpContextAccessor = httpContextAccessor; + } + + public override Task ExecuteAsync(ActionContext context, ObjectResult result) + { + var httpContext = _httpContextAccessor.HttpContext; + var user = httpContext.User.Identity.IsAuthenticated ? httpContext.User.Identity.Name : "Anonymous"; + + //// 动态修改返回的结果数据(示例:修改 Message 字段) + //if (result.Value is ResponseData responseData) + //{ + // if (user == "admin") + // { + // responseData.Message += " (admin)"; + // } + //} + + return base.ExecuteAsync(context, result); + } + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs new file mode 100644 index 0000000..529dfd4 --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/CustomResultFilter.cs @@ -0,0 +1,162 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +using System; +using System.Collections; +using System.Reflection; + +namespace CloudGaming.Code.AppExtend +{ + public class CustomResultFilter : IResultFilter + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public CustomResultFilter(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public void OnResultExecuting(ResultExecutingContext context) + { + // 获取当前的 HttpContext + var httpContext = context.HttpContext; + + // 获取当前用户的信息 + var user = httpContext.User.Identity.IsAuthenticated ? httpContext.User.Identity.Name : "Anonymous"; + + // 获取请求头中的某个信息(例如语言信息) + var language = httpContext.Request.Headers["Accept-Language"].ToString(); + + if (context.Result is ObjectResult objectResult && objectResult.Value != null) + { + if (IsPrimitiveOrString(objectResult.Value.GetType())) + { + + } + // 递归处理返回对象的所有属性并打印路径 + ProcessObjectProperties(objectResult.Value, user, language, ""); + } + } + + public void OnResultExecuted(ResultExecutedContext context) + { + // 可在执行完结果后处理其他逻辑 + } + + // 递归处理对象的所有属性并打印路径 + private void ProcessObjectProperties(object obj, string user, string language, string path) + { + if (obj == null || IsAnonymousType(obj.GetType())) + return; + + var objType = obj.GetType(); + + // 如果对象是集合(数组或列表),递归处理每个元素 + if (obj is IEnumerable enumerable && !(obj is string)) + { + int index = 0; + foreach (var item in enumerable) + { + ProcessObjectProperties(item, user, language, $"{path}[{index}]"); + index++; + } + return; + } + + // 遍历对象的所有属性 + foreach (var property in objType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + // 递归构建属性路径, + var propertyPath = string.IsNullOrEmpty(path) ? property.Name : $"{path}.{property.Name}"; + + // 打印当前属性路径 + Console.WriteLine($"Processing path: {propertyPath}"); + + // 判断是否是只读属性(可读不可写) + if (property.CanRead && !property.CanWrite) + { + // 为只读属性创建可写副本(仅复杂类型处理) + var originalValue = property.GetValue(obj); + if (originalValue != null && !IsPrimitiveOrString(property.PropertyType)) + { + var newValue = CloneAndModifyObject(originalValue, user, language); + // 替换原始对象的只读属性 + ReplaceReadOnlyProperty(obj, property, newValue); + } + } + else if (property.CanRead && property.CanWrite) + { + var propertyValue = property.GetValue(obj); + + // 针对字符串属性进行自定义处理 + if (property.PropertyType == typeof(string)) + { + if (propertyValue is string strValue) + { + property.SetValue(obj, $"{strValue} (modified by {user} with lang {language})"); + } + } + else if (property.PropertyType.IsClass && property.PropertyType != typeof(string)) + { + // 递归处理复杂类型,传递完整的路径 + ProcessObjectProperties(propertyValue, user, language, propertyPath); + } + } + } + } + + // 克隆只读属性的对象,并进行必要的修改 + private object CloneAndModifyObject(object source, string user, string language) + { + var sourceType = source.GetType(); + + // 如果是简单类型或者 string,不克隆 + if (IsPrimitiveOrString(sourceType) || IsAnonymousType(sourceType)) + return source; + + var newInstance = Activator.CreateInstance(sourceType); // 创建新对象实例 + + foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (property.CanRead && property.CanWrite) + { + var value = property.GetValue(source); + // 针对字符串属性进行修改 + if (property.PropertyType == typeof(string) && value is string strValue) + { + property.SetValue(newInstance, $"{strValue} (modified by {user} with lang {language})"); + } + else + { + property.SetValue(newInstance, value); + } + } + } + + return newInstance; + } + + // 替换只读属性的值 + private void ReplaceReadOnlyProperty(object obj, PropertyInfo property, object newValue) + { + var backingField = obj.GetType().GetField($"<{property.Name}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); + if (backingField != null) + { + backingField.SetValue(obj, newValue); + } + } + + // 判断类型是否是基础类型或 string + private bool IsPrimitiveOrString(Type type) + { + return type.IsPrimitive || type == typeof(string) || type == typeof(decimal); + } + + // 判断是否为匿名类型 + private bool IsAnonymousType(Type type) + { + return type.Name.StartsWith("<>f__AnonymousType"); + } + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JsonConverHelper/JsonConverterUtil/StringConverter.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JsonConverHelper/JsonConverterUtil/StringConverter.cs new file mode 100644 index 0000000..94ad9f7 --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JsonConverHelper/JsonConverterUtil/StringConverter.cs @@ -0,0 +1,33 @@ +using Newtonsoft.Json; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.Code.AppExtend.JsonConverHelper.JsonConverterUtil +{ + public class StringConverter : JsonConverter + { + public override bool CanRead => base.CanRead; + + public override bool CanWrite => base.CanWrite; + + public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer) + { + return reader.Value.ToString(); + } + /// + /// 格式化时写入 + /// + /// + /// + /// + public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer) + { + writer.WriteValue(value); + } + + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JsonConverHelper/LanguageContractResolver.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JsonConverHelper/LanguageContractResolver.cs new file mode 100644 index 0000000..cae7b9b --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/JsonConverHelper/LanguageContractResolver.cs @@ -0,0 +1,144 @@ +using Microsoft.AspNetCore.Http; + +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.Code.AppExtend.JsonConverHelper +{ + public class LanguageContractResolver(IServiceCollection serviceProvider) : CamelCasePropertyNamesContractResolver + { + + //public LanguageContractResolver + //{ + // //_httpContextAccessor = httpContextAccessor; + // //CloudGamingBase cloudGamingBase = new CloudGamingBase(serviceProvider); + // //var x = cloudGamingBase.AppConfig; + //} + + + protected override IValueProvider CreateMemberValueProvider(MemberInfo member) + { + if (DynamicCodeGeneration) + { + if (member.MemberType == MemberTypes.Property) + { + Type propType = ((PropertyInfo)member).PropertyType; + + } + } + return base.CreateMemberValueProvider(member); + } + + protected override JsonStringContract CreateStringContract(Type objectType) + { + return base.CreateStringContract(objectType); + } + + protected override string ResolvePropertyName(string propertyName) + { + return base.ResolvePropertyName(propertyName); + } + + + private readonly Stack _propertyPathStack = new Stack(); + + public string CurrentPath => string.Join(".", _propertyPathStack); + //protected virtual JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + protected override JsonProperty CreateProperty(System.Reflection.MemberInfo member, MemberSerialization memberSerialization) + { + + + + //JsonPropertyAttribute? propertyAttribute = JsonTypeReflector.GetAttribute(member); + + + var property = base.CreateProperty(member, memberSerialization); + + var originalWriteJson = property.ValueProvider; + property.ValueProvider = new PathTrackingValueProvider(originalWriteJson, property.PropertyName, this); + + return property; + } + + private class PathTrackingValueProvider : IValueProvider + { + private readonly IValueProvider _innerValueProvider; + private readonly string _propertyName; + private readonly LanguageContractResolver _resolver; + + public PathTrackingValueProvider(IValueProvider innerValueProvider, string propertyName, LanguageContractResolver resolver) + { + _innerValueProvider = innerValueProvider; + _propertyName = propertyName; + _resolver = resolver; + } + + public object GetValue(object target) + { + _resolver._propertyPathStack.Push(_propertyName); + var value = _innerValueProvider.GetValue(target); + + if (value is IEnumerable collection && !(value is string)) + { + int index = 0; + foreach (var item in collection) + { + _resolver._propertyPathStack.Push($"[{index}]"); + // 递归处理集合中的每个元素 + ProcessValue(item, _resolver); + _resolver._propertyPathStack.Pop(); + index++; + } + } + else if (value is not null && !(value is string) && !value.GetType().IsPrimitive) + { + // 递归处理嵌套的对象 + ProcessValue(value, _resolver); + } + else + { + // 处理基本类型(如字符串、整数等) + Console.WriteLine($"Serializing property path: {_resolver.CurrentPath}"); + } + + _resolver._propertyPathStack.Pop(); + return value; + } + + public void SetValue(object target, object value) + { + _innerValueProvider.SetValue(target, value); + } + + private void ProcessValue(object value, LanguageContractResolver resolver) + { + var contract = resolver.ResolveContract(value.GetType()); + if (contract is JsonObjectContract objectContract) + { + foreach (var property in objectContract.Properties) + { + var provider = property.ValueProvider; + var propertyValue = provider?.GetValue(value); + if (propertyValue != null) + { + resolver._propertyPathStack.Push(property.PropertyName); + Console.WriteLine($"Serializing property path: {resolver.CurrentPath}"); + resolver._propertyPathStack.Pop(); + } + } + } + } + } + + + } +}