WireMock gRPC扩展使用google.protobuf.StringValue时抛无效包装类型异常
WireMock gRPC扩展处理Protobuf包装类型字段报错问题
我基于WireMock的gRPC演示项目开发,定义了包含google.protobuf.StringValue包装类型的Protobuf协议:
syntax = "proto3"; option csharp_namespace = "My.Package"; option java_package = "com.my.package"; import "google/protobuf/wrappers.proto"; service MySvc { rpc GetBond(MyRequest) returns (MyResponse) {} } message MyRequest { string myValue = 1; } message MyResponse { bool success = 1; string message = 2; google.protobuf.StringValue messageStringValue = 3; }
改写测试类GrpcTest.java如下:
import com.example.grpc.HelloRequest; import com.example.grpc.HelloResponse; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.google.protobuf.StringValue; import com.mine.My; import com.mine.MySvcGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.wiremock.grpc.GrpcExtensionFactory; import org.wiremock.grpc.dsl.WireMockGrpc; import org.wiremock.grpc.dsl.WireMockGrpcService; import wiremock.grpc.client.GreetingsClient; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static java.lang.Thread.sleep; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.wiremock.grpc.dsl.WireMockGrpc.Status; import static org.wiremock.grpc.dsl.WireMockGrpc.equalToMessage; import static org.wiremock.grpc.dsl.WireMockGrpc.message; import static org.wiremock.grpc.dsl.WireMockGrpc.method; public class GrpcTest { WireMockGrpcService mockGreetingService; WireMockGrpcService mockRefDataService; ManagedChannel channel; GreetingsClient greetingsClient; @RegisterExtension public static WireMockExtension wm = WireMockExtension.newInstance() .options( wireMockConfig() .port(9090) .withRootDirectory("src/test/resources/wiremock") .extensions(new GrpcExtensionFactory()) ) .build(); @BeforeEach void init() { var wiremock = new WireMock(wm.getPort()); mockRefDataService = new WireMockGrpcService(wiremock, MySvcGrpc.SERVICE_NAME); } @AfterEach void tearDown() { channel.shutdown(); } @Test void dynamic_response_via_JSON() throws InterruptedException { mockRefDataService.stubFor( method("GetResponse") .willReturn(WireMockGrpc.message( My.MyResponse.newBuilder() .setSuccess(true) .setMessage("abc") .setMessageStringValue(StringValue.of("ASDAS")) )) ); System.out.println("START"); sleep(30000); } }
使用Postman向gRPC Mock Server发送请求时,抛出以下异常:
Exception in thread "grpc-default-executor-0" com.google.protobuf.InvalidProtocolBufferException: Invalid wrapper type: google.protobuf.StringValue at com.google.protobuf.util.JsonFormat$ParserImpl.mergeWrapper(JsonFormat.java:1616) at com.google.protobuf.util.JsonFormat$ParserImpl.access$2100(JsonFormat.java:1276) at com.google.protobuf.util.JsonFormat$ParserImpl$2.merge(JsonFormat.java:1352) at com.google.protobuf.util.JsonFormat$ParserImpl.merge(JsonFormat.java:1432) at com.google.protobuf.util.JsonFormat$ParserImpl.parseFieldValue(JsonFormat.java:1995) at com.google.protobuf.util.JsonFormat$ParserImpl.mergeField(JsonFormat.java:1646) at com.google.protobuf.util.JsonFormat$ParserImpl.mergeMessage(JsonFormat.java:1477) at com.google.protobuf.util.JsonFormat$ParserImpl.merge(JsonFormat.java:1435) at com.google.protobuf.util.JsonFormat$ParserImpl.merge(JsonFormat.java:1317) at com.google.protobuf.util.JsonFormat$Parser.merge(JsonFormat.java:449) at org.wiremock.grpc.internal.JsonMessageUtils.lambda$toMessage$1(JsonMessageUtils.java:36) at com.github.tomakehurst.wiremock.common.Exceptions.uncheck(Exceptions.java:72) at org.wiremock.grpc.internal.JsonMessageUtils.toMessage(JsonMessageUtils.java:36) at org.wiremock.grpc.internal.UnaryServerCallHandler.lambda$invoke$0(UnaryServerCallHandler.java:92) at com.github.tomakehurst.wiremock.http.AbstractRequestHandler.handle(AbstractRequestHandler.java:101) at org.wiremock.grpc.internal.UnaryServerCallHandler.invoke(UnaryServerCallHandler.java:60) at org.wiremock.grpc.internal.UnaryServerCallHandler.invoke(UnaryServerCallHandler.java:31) at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:182) at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:351) at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:860) at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833)
依赖版本信息
初始使用的依赖:
java.io java.lang java.util Gradle: com.google.protobuf:protobuf-java:3.24.0 Gradle: io.grpc:grpc-api:1.59.0 Gradle: org.hamcrest:hamcrest:2.2 Gradle: org.junit.jupiter:junit-jupiter-api:5.10.0 Gradle: org.wiremock:wiremock-grpc-extension:0.3.0 Gradle: org.wiremock:wiremock:3.3.1
升级后的依赖(问题仍存在):
java.io java.lang java.util Gradle: com.google.protobuf:protobuf-java:3.25.1 Gradle: io.grpc:grpc-api:1.63.0 Gradle: org.hamcrest:hamcrest:2.2 Gradle: org.junit.jupiter:junit-jupiter-api:5.10.0 Gradle: org.wiremock:wiremock-grpc-extension:0.6.0 Gradle: org.wiremock:wiremock:3.3.1
测试过程中的发现
- 从
willReturn()中移除.setMessageStringValue(StringValue.of("ASDAS"))后,请求可正常收到响应 - 单独构建
WireMockGrpc.message()时,生成的内容不符合预期:
生成的Body内容:WireMockGrpc.message( My.MyResponse.newBuilder() .setSuccess(true) .setMessage("abc") .setMessageStringValue(StringValue.of("ASDAS")) ).build();Body {content={ "success": true, "message": "abc", "messageStringValue": "ASDAS" <- 此处应该是{"value": "ASDAS"} }, binary=false, json=false} - 改用
.setMessageStringValue(StringValue.newBuilder().setValue("asda").build())也无法解决问题
问题分析及解决方案
从错误日志和生成的JSON结构可以看出,WireMock gRPC扩展在将Protobuf消息转换为JSON时,没有正确处理Google官方的包装类型(如StringValue),而是直接将其序列化为原始字符串,导致后续解析时抛出Invalid wrapper type异常。
临时解决方案:
- 规避方案:如果业务允许,暂时移除包装类型字段,改用普通字符串类型
- 手动构造JSON响应:跳过
WireMockGrpc.message()方法,直接构造符合Protobuf JSON格式的响应内容:mockRefDataService.stubFor( method("GetResponse") .willReturn(WireMockGrpc.jsonMessage("{\"success\":true,\"message\":\"abc\",\"messageStringValue\":{\"value\":\"ASDAS\"}}")) ); - 反馈问题:向WireMock gRPC扩展项目提交Issue,等待官方修复该兼容性问题
内容的提问来源于stack exchange,提问作者yavorek
相关产品推荐
相关产品推荐

