自定义Protobuf Decimal类型在gRPC+REST下的返回异常问询
自定义Protobuf Decimal类型在gRPC REST接口返回异常的解决方案
你遇到的问题并非正常行为,核心原因是.NET的gRPC HTTP API默认依赖的System.Text.Json序列化机制,无法自动识别自定义的Protobuf DecimalValue类型,导致返回JSON时字段被错误重置为0。你的需求完全可以实现,只需为自定义类型配置JSON转换器并正确注册,同时验证映射逻辑。
解决方案步骤
1. 为DecimalValue实现JSON转换器
创建一个自定义JsonConverter,处理DecimalValue与JSON的双向序列化:
using System.Text.Json; using System.Text.Json.Serialization; using CustomType.V1; // 替换为你的DecimalValue所在命名空间 public class DecimalValueJsonConverter : JsonConverter<DecimalValue> { public override DecimalValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException("Expected object for DecimalValue"); long units = 0; int nanos = 0; while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) break; if (reader.TokenType != JsonTokenType.PropertyName) throw new JsonException("Expected property name"); var propName = reader.GetString(); reader.Read(); switch (propName) { case "units": units = reader.GetInt64(); break; case "nanos": nanos = reader.GetInt32(); break; default: reader.Skip(); break; } } return new DecimalValue { Units = units, Nanos = nanos }; } public override void Write(Utf8JsonWriter writer, DecimalValue value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteNumber("units", value.Units); writer.WriteNumber("nanos", value.Nanos); writer.WriteEndObject(); } }
2. 注册转换器到gRPC HTTP API配置
在Program.cs中找到gRPC相关配置,添加转换器到JSON转码选项:
builder.Services.AddGrpc() .AddJsonTranscoding(options => { options.JsonSerializerOptions.Converters.Add(new DecimalValueJsonConverter()); });
如果同时使用普通ASP.NET Core API,也要在控制器的JSON配置中注册:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new DecimalValueJsonConverter()); });
3. 验证AutoMapper映射逻辑
你使用Adapt<FindFooResponse>进行模型转换,需确保领域模型到FooRpc的映射正确处理了货币值到DecimalValue的转换。比如领域模型有decimal Price属性,映射配置应类似:
CreateMap<FooDomain, FooRpc>() .ForMember(dest => dest.Price, opt => opt.MapFrom(src => { var price = src.Price; long units = (long)price; int nanos = (int)((price - units) * 1_000_000_000); return new DecimalValue { Units = units, Nanos = nanos }; }));
4. 快速排查测试
直接构造DecimalValue实例返回,排除映射问题:
public override async Task<FindFooResponse> FindFoo(Empty request, ServerCallContext context) { return new FindFooResponse { Foo = { new FooRpc { Price = new DecimalValue { Units = 200, Nanos = 990000000 } } } }; }
如果此测试返回正确JSON,说明问题出在映射逻辑而非序列化配置。
内容的提问来源于stack exchange,提问作者José Romero
相关产品推荐
相关产品推荐

