验证多语言方向
This commit is contained in:
parent
08d451e061
commit
145ae55d96
|
|
@ -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" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user