RSS

gRPC-Web のインターセプター

リリース 1.1.0 より、gRPC-web でのインターセプターのサポートを発表できることを嬉しく思います。現在の設計は、他の gRPC 言語で利用可能な gRPC クライアントインターセプターに基づいていますが、モダンな Web フレームワークと組み合わせてインターセプターを簡単に導入して使用できるように、gRPC-web 固有の機能も含まれています。

概要

他の gRPC 言語と同様に、gRPC-web は単項およびサーバーストリーミングインターセプターをサポートしています。各インターセプターの種類について、単一の `intercept()` メソッドを含むインターフェースを定義しました

  • UnaryInterceptor
  • StreamInterceptor

これは `UnaryInterceptor` インターフェースの宣言方法です。

/*
* @interface
*/
const UnaryInterceptor = function() {};

/**
 * @template REQUEST, RESPONSE
 * @param {!Request<REQUEST, RESPONSE>} request
 * @param {function(!Request<REQUEST,RESPONSE>):!Promise<!UnaryResponse<RESPONSE>>}
 *     invoker
 * @return {!Promise<!UnaryResponse<RESPONSE>>}
 */
UnaryInterceptor.prototype.intercept = function(request, invoker) {};

`intercept()` メソッドは 2 つのパラメータを取ります。

  • `grpc.web.Request` 型の `request`
  • 実際のエラー RPC を実行する `invoker`

`StreamInterceptor` インターフェースの宣言も同様ですが、`invoker` の戻り値の型が `Promise` ではなく `ClientReadableStream` になる点が異なります。実装の詳細については、interceptor.js を参照してください。

インターセプターで何ができるか?

インターセプターを使用すると、次のことが可能になります。

  • RPC を渡す前に元の gRPC リクエストを更新する — 例えば、認証ヘッダーのような追加情報を注入できます。
  • 元の invoker 関数の動作を操作する — 例えば、キャッシュされた結果を使用できるように呼び出しをバイパスするなど。
  • クライアントに返される前にレスポンスを更新する。

次に例を示します。

単項インターセプターの例

以下に示すコードは、次の処理を行う単項インターセプターを示しています。

  • RPC の前に gRPC リクエストメッセージに文字列を付加します。
  • 受信後に gRPC レスポンスメッセージに文字列を付加します。

このシンプルな単項インターセプターは、`UnaryInterceptor` インターフェースを実装するクラスとして定義されます。

/**
 * @constructor
 * @implements {UnaryInterceptor}
 */
const SimpleUnaryInterceptor = function() {};

/** @override */
SimpleUnaryInterceptor.prototype.intercept = function(request, invoker) {
  // Update the request message before the RPC.
  const reqMsg = request.getRequestMessage();
  reqMsg.setMessage('[Intercept request]' + reqMsg.getMessage());

  // After the RPC returns successfully, update the response.
  return invoker(request).then((response) => {
    // You can also do something with response metadata here.
    console.log(response.getMetadata());

    // Update the response message.
    const responseMsg = response.getResponseMessage();
    responseMsg.setMessage('[Intercept response]' + responseMsg.getMessage());

    return response;
  });
};

ストリームインターセプターの例

`ClientReadableStream` を使用してサーバーからストリーミングされるレスポンスをインターセプトするには、`StreamInterceptor` を使用してより注意が必要です。主な手順は次のとおりです。

  1. `ClientReadableStream` ラッパー クラスを作成し、それを使用してサーバーレスポンスの受信などのストリームイベントをインターセプトします。
  2. `StreamInterceptor` を実装し、ストリームラッパーを使用するクラスを作成します。

次のサンプルストリーム ラッパークラスは、レスポンスをインターセプトし、レスポンスメッセージに文字列を付加します。

/**
 * A ClientReadableStream wrapper.
 *
 * @template RESPONSE
 * @implements {ClientReadableStream}
 * @constructor
 * @param {!ClientReadableStream<RESPONSE>} stream
 */
const InterceptedStream = function(stream) {
  this.stream = stream;
};

/** @override */
InterceptedStream.prototype.on = function(eventType, callback) {
  if (eventType == 'data') {
    const newCallback = (response) => {
      // Update the response message.
      const msg = response.getMessage();
      response.setMessage('[Intercept response]' + msg);
      // Pass along the updated response.
      callback(response);
    };
    // Register the new callback.
    this.stream.on(eventType, newCallback);
  } else {
    // You can also override 'status', 'end', and 'error' eventTypes.
    this.stream.on(eventType, callback);
  }
  return this;
};

/** @override */
InterceptedStream.prototype.cancel = function() {
  this.stream.cancel();
  return this;
};

サンプルインターセプターの `intercept()` メソッドは、ラップされたストリームを返します。

/**
 * @constructor
 * @implements {StreamInterceptor}
 */
const TestStreamInterceptor = function() {};

/** @override */
TestStreamInterceptor.prototype.intercept = function(request, invoker) {
  return new InterceptedStream(invoker(request));
};

インターセプターのバインド

適切なオプションキーを使用してインターセプターインスタンスの配列を渡すことで、クライアントインスタンス化時にインターセプターをクライアントにバインドできます。

const promiseClient = new MyServicePromiseClient(
    host, creds, {'unaryInterceptors': [interceptor1, interceptor2, interceptor3]});

const client = new MyServiceClient(
    host, creds, {'streamInterceptors': [interceptor1, interceptor2, interceptor3]});

フィードバック

grpc-web で問題が見つかった、または機能が必要ですか?grpc-web リポジトリに問題 (issue) を報告してください。一般的な質問やコメントがある場合は、gRPC メーリングリスト に投稿するか、grpc-web-team@google.com までメールでお問い合わせください。