protobufをHTTP通信する
protocol bufferは通常gRPCで利用しますが、API定義をprotoファイルで行い 通信自体はHTTPで行いたい、というケースが実務でありましたので方法を記載します。
検証環境
software | version |
---|---|
OS | MacOS Mojave |
Spring Boot | 2.1.5 |
Java | 1.8.0_192-b12 |
Kotlin | 1.3.40 |
com.google.protobuf | 0.8.8 |
protobufを送信する
content-typeを「application/x-protobuf」 にすれば普通に送れます。基本これでOK
syntax = "proto3"; option java_multiple_files = true; option java_package = "com.example.demo.web.proto"; import "google/protobuf/timestamp.proto"; package demo; message Foo { FooId id = 1; repeated FooProperty property = 2; google.protobuf.Timestamp createdAt = 3; } message FooId { int32 id = 1; } message FooProperty { string code = 1; string name = 2; }
// クライアント suspend fun requestByProtobuf(foo: Foo): FooResponse { val client = WebClient.builder() .baseUrl("http://localhost:8081") .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/x-protobuf") .build() return client .post() .uri("foo/proto") .body(BodyInserters.fromObject(foo)) .exchange() .flatMap { it.bodyToMono(FooResponse::class.java) } .awaitFirst() } // サーバ @PostMapping( "foo/proto" ) fun test( @RequestBody foo: Foo ): FooResponse { return FooResponse.newInstance(foo) }
Jsonを送信する
受け取り側でJsonをprotoにmappingするためにProtobufHttpMessageConverter
をBeanに設定します。
// 受け取り側にBeanを追加 @Configuration class ProtobufConverterConfig { @Bean fun protobufHttpMessageConverter(): ProtobufHttpMessageConverter { val printer = JsonFormat.printer().omittingInsignificantWhitespace() val parser = JsonFormat.parser().ignoringUnknownFields() return ProtobufJsonFormatHttpMessageConverter(parser, printer) } }
ignore fieldが含まれる場合は無視するように設定しています。 後は普通にJsonを送信するだけで適切にMappingされます。
suspend fun requestByJson(fooJson: String): FooResponse { val client = WebClient.builder() .baseUrl("http://localhost:8081") .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) .build() return client .post() .uri("foo/proto") .body(BodyInserters.fromObject(fooJson)) .exchange() .flatMap { it.bodyToMono(FooResponse::class.java) } .awaitFirst() }
まとめ
protobufをHTTP送信する方法でした。通常はx-protobufでやり取りするのがシンプルで良いと思います。