4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Flutter】 AWS APIGatewayのWebSocket APIを使ってみた

Last updated at Posted at 2023-05-15

FlutterでAWS API GatewayのWebSocket APIを使ってみたい!という方向けの記事になります。
本記事は実装方法に特化して書いていますので、そもそもWebSoketって?という方は参考記事をご参照ください。

前提

  • Flutterの環境構築は済んでおり、基礎は知っている
  • AWSアカウント作成済み

構成

手順

大まかな手順としては↓になります。

  1. Lambdaに関数を作成
  2. APIGatewayでAPIを作成
  3. Flutter側の実装

Lambdaに関数を作成

関数の作成

Lambdaのコンソールを開き「関数の作成」を選択します。
関数.png
各種設定を選択。
「一から作成」を選択。

項目名 設定内容
関数名 好きな関数名を入力
ランタイム Node.js 16.x
アーキテクチャ x86_64

(他の項目は特に設定しなくても問題ありません)
関数の作成.png
「関数の作成」を押すと、関数が作成されます。

各種処理追加

作成した関数を開き、ディレクトリ構成を以下のようにします。
【】→フォルダ名

  • 【関数名】
    • 【interface】
      • connect.js
      • disconnect.js
      • sendText.js
    • index.js

index.js

index.js
const { connect } = require('./interface/connect');
const { sendText } = require('./interface/sendText');
const { disconnect } = require('./interface/disconnect'); 

exports.handler = async (event, context) => {
	// レスポンスデータ
    let response;
    // イベントタイプ         
    let routeKey;          
        
    //-------------------------------------
    //-- eventTypeからIF毎のロジックに振り分け
    //-------------------------------------
    routeKey = event.requestContext.routeKey;

    switch (routeKey){
        
        case "$connect":
            //***************************************************
            //** 接続
            //***************************************************
            response = await connect(event, context);
            break;
            
        case "sendText":
            //***************************************************
            //** 文字送信
            //***************************************************
            response = await sendText(event, context);
            break;
            
        case "$disconnect":
            //***************************************************
            //** 切断
            //***************************************************
            response = await disconnect(event, context);
            break;
    }

    return response_final;

};

connect.js

connect.js
exports.connect = async (event, context, callback) => {
    // ***************************************************
    // ** 接続時の処理を好きに追加
    // ***************************************************
    
    // レスポンスデータの生成
    let response = {
        "statusCode": event.statusCode,
        "headers": event.headers,
        "body": context.body,
        "isBase64Encoded": false
    };
    return response;
};

disconnect.js

disconnect.js
exports.disconnect = async (event, context, callback) => {
    // ***************************************************
    // ** 切断時の処理を好きに追加
    // ***************************************************
    
    // レスポンスデータの生成
    let response = {
        "statusCode": event.statusCode,
        "headers": event.headers,
        "body": context.body,
        "isBase64Encoded": false
    };
    return response;
};

sendText.js

sendTextが呼ばれた時に「送信されてきたdataを、送信者にそのまま送信する」ようにしています。

sendText.js
const { ApiGatewayManagementApiClient, PostToConnectionCommand } = require("@aws-sdk/client-apigatewaymanagementapi");

exports.sendText = async (event, context, callback) => {
    // ***************************************************
    // ** 実装例)sendText実行時の処理
    // ***************************************************
    // apigateway client インスタンス取得
	const domain = event.requestContext.domainName;
    const stage = event.requestContext.stage;
    const endpoint = `https://${domain}/${stage}`;
    const agmClient = new ApiGatewayManagementApiClient({ endpoint: endpoint });
    
    //----------------------------------------------
    //-- clientにdata送信
    //----------------------------------------------
    // clientに送信するデータ
	const body = JSON.parse(event.body);    
	const sendData = {
        text : body.data.text
    };
    
    const sendParams = {
        Data: Buffer.from(JSON.stringify(sendData))
    };
	// 送信先のconnectionId
    sendParams.ConnectionId = event.requestContext.connectionId;
    
    try {
      // クライアントに送信
      response = await agmClient.send( new PostToConnectionCommand(sendParams));
      // 送信成功時の処理
				      
    } catch (error) {
      // 送信失敗時の処理        
    }
    
    // レスポンスデータの生成
    response = {
        "statusCode": event.statusCode,
        "headers": event.headers,
        "body": context.body,
        "isBase64Encoded": false
    };
    return response;
};

Deploy

「Deploy」ボタンを押します。
これやらないと反映されないので、お忘れなく。
デプロイ.png

実行ロールの追加

LambdaからAPIGatewayにアクセスできるように、実行ロールを追加します。
ロール.png
編集内容は以下の公式ドキュメントを参照
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-websocket-control-access-iam.html

APIGatewayでAPIを作成

APIの作成

AWS API Gatewayのコンソールに行き「APIの作成」を選択します。
APIを作成.png
今回作成するのはWebSocket APIなので「WebSocket API」の「構築」を押します。
APIタイプを選択.png

APIの詳細を指定する

項目名 設定内容
API名 好きなAPI名を入力
ルート選択式 「request.body.action」を入力

詳細を指定.png
入力したら「次へ」ボタンを押します。

ルートを追加

  1. $connectルートの追加 $disconnectルートの追加 $defaultルートの追加を押下。
  2. 「カスタムルートを追加」を押し、入力欄に「sendText」と入力します。
  3. 「次へ」ボタンを押下。
    ルートの追加.png

統合をアタッチする

すべてのルートに対して同じLambda関数を設定します。
どのルートから呼び出されているかはLambda側で判定するため、同じもので問題ありません。

項目名 設定内容
統合タイプ Lambda
AWS リージョン 使用しているリージョン
Lambda関数 呼び出す関数(先ほど作成したもの)

統合のアタッチ.png
統合タイプ.png

ステージの追加

ステージの追加.png

項目名 設定内容
ステージ名 好きなステージ名を入力

確認して作成

  1. 設定内容を確認
  2. 「作成してデプロイ」を押下
    確認して作成.png

Flutter側の実装

パッケージの追加

dependencies:
  flutter:
    sdk: flutter

  # WebSoket
  web_socket_channel: ^2.2.0

接続

接続用のインスタンスの作成

// WebSocketインスタンス
WebSocketChannel? _channel;

使用したい画面のライフサイクルメソッドなどで、以下のメソッドを呼びます。

// WebSocket接続開始
void startConnection(){
	_channel = IOWebSocketChannel.connect("wss://xxxxxxxxx");
	
	// 切断時の監視
	final subscription = _channel?.stream.listen(null);
	subscription?.onDone(() {
	  // WebSocket切断時の処理
	});
}

接続先のURLはAPIGatewayで以下の画面の「WebSocket URL」を使用します。
wssから始まるやつです。
URL.png

サーバーを監視し、変更があれば取得・表示できるようにします。
ここでは受け取ったdataをTextに変換して表示しておきます。

StreamBuilder(
	stream: _channel?.stream,
	builder: (context, snapshot) {
	  return Text(snapshot.hasData
	      ? 'response:${snapshot.data}'
	      : 'response:');
},

切断

切断する時は以下のメソッドを呼ぶだけです。

void disconnect() async {
  await _channel?.sink.close();
}

送信

APIGateway WebSocket APIの場合は、ただデータを送るだけでなく、ルートを指定する必要があります。
以下のfunctionを、ボタン押下時などに呼び出してください。

// 文字送信
void sendTextAction() {   
  Map data = {
	// 送信したいAPIのルート名を指定
    "action": "sendText",
	// 送信するデータ    
	"data":
    {
      "text": "文字送ったよ",
    } 
  };
  // 送信処理
  _channel?.sink.add(jsonEncode(data));      
}

まとめ

今回苦戦したのがAWS周りだったので、Flutterはだいぶ省略してしまいました。お許しください。
どなたかの一助となれば幸いです。

参考記事

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?