dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
34.85k stars 9.84k forks source link

gRPC JSON transcoding google.protobuf.WellKnownTypes response/request cast error #55254

Open aspmaker opened 2 months ago

aspmaker commented 2 months ago

Hello everyone. I am registering to gRPC JSON transcoding in my program.cs. And i am using in my any .proto file message-property with google.protobuf.WellKnownTypes (in example: google.protobuf.StringValue). Because i want use nullable params. When i request to the gRPC service with http/1.1, it gives me cast error in it's response.

{
    "code": 3,
    "message": "Unable to cast object of type 'Google.Protobuf.WellKnownTypes.StringValue' to type 'System.String'.",
    "details": []
}
`Grpc.AspNetCore.Grpc.JsonTranscoding.ServerCallHandler[2]
      Error when executing service method 'SayHello'.
      System.InvalidCastException: Unable to cast object of type 'Google.Protobuf.WellKnownTypes.StringValue' to type 'System.String'.
         at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.<>c__DisplayClass9_1.<SetGetter>b__1(Object obj)
         at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
         at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
         at Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.JsonRequestHelpers.WriteResponseMessage(HttpResponse response, Encoding encoding, Object responseBody, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.JsonRequestHelpers.WriteResponseMessage(HttpResponse response, Encoding encoding, Object responseBody, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.JsonRequestHelpers.SendMessage[TResponse](JsonTranscodingServerCallContext serverCallContext, JsonSerializerOptions serializerOptions, TResponse message, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.JsonRequestHelpers.SendMessage[TResponse](JsonTranscodingServerCallContext serverCallContext, JsonSerializerOptions serializerOptions, TResponse message, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.CallHandlers.UnaryServerCallHandler`3.HandleCallAsyncCore(HttpContext httpContext, JsonTranscodingServerCallContext serverCallContext)
         at Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.CallHandlers.ServerCallHandlerBase`3.<HandleCallAsync>g__AwaitHandleCall|14_0(JsonTranscodingServerCallContext serverCallContext, Method`2 method, Boolean isStreaming, JsonSerializerOptions serializerOptions, Task handleCall)`

Example files and codes; .csproj file:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <IncludeHttpRuleProtos>true</IncludeHttpRuleProtos>
  </PropertyGroup>

  <ItemGroup>
      <Protobuf Include="Protos\greet.proto" GrpcServices="both" ProtoRoot="Protos" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.62.0" />
    <PackageReference Include="Microsoft.AspNetCore.Grpc.JsonTranscoding" Version="9.0.0-preview.3.24172.13" />
  </ItemGroup>

</Project>

program.cs:

builder.Services
    .AddGrpc()
    .AddJsonTranscoding();

My proto file:

syntax = "proto3";

option csharp_namespace = "H.GrpcService";

import "Google/protobuf/wrappers.proto"; 
import "Google/api/annotations.proto"; 

package greet;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v1/greeter",
        body : "*"
    };
  };
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  google.protobuf.StringValue message = 1;
}

postman request json:

{
  "name": "John Doe"
}

gRPC method:

public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
    return Task.FromResult(new HelloReply
    {
        Message = new Google.Protobuf.WellKnownTypes.StringValue { Value = "H.Hello " + request.Name }
    });
}

We can only use primitive types or what is the wrong with this usage, pls can you help me. Thank you.

dotnet-policy-service[bot] commented 2 months ago

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis See info in area-owners.md if you want to be subscribed.