xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

gRPC / MagicOnion 入門 (10) - ヘッダーの利用

HTTP/2 をベースとする gRPC にもヘッダーがあり、そこに任意のデータを含めて通信することができます。今回はそんなヘッダーをどうやって利用するのか、その方法について解説します。

ヘッダーにデータを詰めて送信する

ヘッダーは Key-Value 形式となっており、文字列か byte 配列 のいずれかの値を詰めることができます。データは以下のように Metadata 型に格納し、WithHeaders メソッドでヘッダーに追加します。

using System;
using System.Threading.Tasks;
using Grpc.Core;
using MagicOnion.Client;
using MagicOnionSample.ServiceDefinition;

namespace MagicOnionSample.Client
{
    class Program
    {
        static void Main() => MainAsync().Wait();

        static async Task MainAsync()
        {
            var channel = new Channel("localhost", 12345, ChannelCredentials.Insecure);

            //--- ヘッダーに詰めるメタデータを生成
            var metadata = new Metadata();
            metadata.Add("Key", "Value");
            metadata.Add("Key-bin", new byte[]{ 1, 2 });

            /*
            //--- この書き方でも OK
            var metadata = new Metadata()
            {
                new Metadata.Entry("Key", "Value"),
                new Metadata.Entry("Key-bin", new byte[]{ 1, 2 }),
            };
            */

            //--- ヘッダーにメタデータを追加
            var client = MagicOnionClient.Create<ISampleApi>(channel).WithHeaders(metadata);

            //--- あとはいつも通り通信しましょう
            var result = await client.Sample();

            //--- アプリが終了しないように
            Console.ReadLine();
        }
    }
}

サーバー側でヘッダーに格納されたデータを取り出す場合は CallContext.RequestHeaders を利用します。例えば以下のようになります。

using System;
using MagicOnion;
using MagicOnion.Server;
using MagicOnionSample.ServiceDefinition;
using MessagePack;

namespace MagicOnionSample.Service
{
    public class SampleApi : ServiceBase<ISampleApi>, ISampleApi
    {
        public async UnaryResult<Nil> Sample()
        {
            //--- ヘッダーから値を取り出す
            var header = this.Context.CallContext.RequestHeaders;
            var value1 = header.Get("Key").Value;
            var value2 = header.Get("Key-bin").ValueBytes;

            Console.WriteLine(value1);
            Console.WriteLine($"{{{value2[0]}, {value2[1]}}}");

            return Nil.Default;
        }
    }
}

たったこれだけでヘッダーを経由したデータの送受信ができました。簡単ですね!

バイナリデータを送信する場合の注意

ヘッダーに文字列を詰めて送信する場合は特に何も気にしなくて良いのですが、バイナリ (byte 配列) を扱う場合はキー名に -bin の接尾辞を付けるというルールがあるので注意が必要です。-bin なしでキーを設定すると以下のような例外が発生します。

Key for binary valued metadata entry needs to have suffix indicating binary value.

ちなみに -bin という文字列は gRPC のライブラリに Metadata.BinaryHeaderSuffix として定義されているので、それを利用しても OK です。

metadata.Add("Key-bin", new byte[]{ 1, 2 });
metadata.Add($"Key{Metadata.BinaryHeaderSuffix}", new byte[]{ 1, 2 });

ヘッダーの使いどころ

そんなヘッダーの利用タイミングですが、たとえば以下のようなときに便利です。困ったときの最終手段として覚えておくと良さそうです。

  • Client Streaming 通信 / Duplex Streaming 通信における引数の代替*1
  • 例外発生時のエラーメッセージの伝搬 *2
  • すべての通信で利用する ID など *3

*1:この 2 通信では引数を利用できないため、その回避方法として

*2:MagicOnion はエラー詳細の送信にヘッダーを利用

*3:MagicOnion は Connection ID の送信にヘッダーを利用