验证多语言方向

This commit is contained in:
zpc 2024-10-14 04:56:49 +08:00
parent 08d451e061
commit 145ae55d96
6 changed files with 421 additions and 5 deletions

View File

@ -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
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<object> GetConfig()
public async Task<dynamic> GetConfig()
{
return "a";
return new { test = "测试", Data = new { txt = "内部数据" } };
}
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<BaseResponse<T_App_Config>> GetConfig1()
{
return new HuanMeng.DotNetCore.Base.BaseResponse<T_App_Config>(ResonseCode.Success, "成功", new T_App_Config() { BossId = "aa" });
}
}
}

View File

@ -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<ExceptionMiddleware>), serviceProvi
return loggerFactory.CreateLogger<ExceptionMiddleware>();
});
#endregion
#region
builder.Services.AddControllers()
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor(); //添加httpContext注入访问
#region
//services.AddControllers(options =>
//{
// // 添加自定义的 ResultFilter 到全局过滤器中
// options.Filters.Add<CustomResultFilter>();
//});
builder.Services.AddControllers(options =>
{
// 添加自定义的 ResultFilter 到全局过滤器中
options.Filters.Add<CustomResultFilter>();
})
.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<ObjectResultExecutor, CustomObjectResultExecutor>();
#endregion
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();

View File

@ -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> 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);
}
}
}

View File

@ -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");
}
}
}

View File

@ -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<string>
{
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();
}
/// <summary>
/// 格式化时写入
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="serializer"></param>
public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}

View File

@ -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<string> _propertyPathStack = new Stack<string>();
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<JsonPropertyAttribute>(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();
}
}
}
}
}
}
}