SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Flutterで始めるモバイルアプリ開発

【Flutterで画面の連携機能を作る】サーバにhttpリクエストをする方法とは?

Flutterで始めるモバイルアプリ開発 第22回

  • X ポスト
  • このエントリーをはてなブックマークに追加

API処理するhttpクライアントを作る

 実際のAPI処理をする場合に、このように毎回get関数やpost関数などを使う場合、同じようなコードを多く記述する必要があります。そこで、共通する処理を行う場合のクラス実装例がリスト6です。

[リスト6]APIに関連するhttpリクエストを行うクラスの実装例(lib/client/ApiClient.dartの抜粋)
import 'dart:async';
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:chat_app/data/Response.dart';

// (1) http.BaseClientを継承する
class ApiClient extends http.BaseClient{

  final String apiKey;
  final String baseUrl;
  final http.Client _client;

  ApiClient(this.apiKey,this.baseUrl,this._client);

  // (2) 共通するリクエストの前処理
  @override
  Future send(http.BaseRequest request) {
    request.headers['Content-Type'] = 'application/json';
    request.headers['X-Chat-App-Key'] = apiKey;
    return _client.send(request);
  }

  /*
   *  初期設定の取得
   */
  Future config() async{
    var url = Uri.parse(baseUrl + "/login/config.php");
    var res = get(url);
    // (3) レスポンスを処理する共通化処理
    var response = await _parseResponse(res);
    if(response.ok){
      return Future.value(true);
    }
    else{
      return Future.value(false);
    }
  }

  // : (省略)

  // (4) 共通するレスポンス処理
  Future _parseResponse(Future response){
    // (5) Future処理ができるようにする
    var c = Completer();
    // (6) httpレスポンス結果の処理
    response.then((value){
      if(value.statusCode == 200){
        var json = jsonDecode(utf8.decode(value.bodyBytes)) as Map;
        if(json['ok']){
          // (7) データの設定
          c.complete(Future.value(Response.ok(json['code'], json['message'],json['body'])));
        }
        else{
          c.complete(Response.error(json['code'], json['message']));
        }
      }
      else{
        c.complete(Response.error(value.statusCode, value.body));
      }
    });
    // (8) Futureインスタンス
    return c.future;
  }
}

 リクエスト処理を共通化するためには、(1)のようにhttp.BaseClientクラスを継承したクラスを作成します。このクラスを実装する場合には、(2)のsendメソッドを実装する必要があります。このメソッドでは、リクエストに関する共通処理を実装できます。

 今回は、Content-TypeとX-Chat-App-Keyというヘッダを共通処理として設定しています。また、レスポンス処理も共通部分が多いため、(3)のように共通メソッドを作って処理します。その実装部分が(4)です。

 共通処理では、httpパッケージでのFutureインスタンスを引数で設定し、アプリケーションでの独自クラスのFutureインスタンスを返すようにします。つまり、Futureで扱うデータクラスの変換を行う処理をしています。

 また、その際に、async/awaitキーワードを使わないようにしました。そのためには、(5)の用にCompleterクラスを使います。このクラスは前回紹介したFutureクラスのメソッドでは作れないようなFutureを作ることができます。

 例えば、今回のようにデータ準備が他の非同期処理の結果を待って、新たなFutureインスタンスを作成したい場合に利用します。JavaScriptではPromiseのような役割と言えば伝わる人も多いかも知れません。httpレスポンスを受け取れる状態になったら(6)のようにthenメソッドを使って処理をします。

 そして、返却するFutureのデータの準備ができたら、(7)のようにcompleteメソッドを使って結果を設定します。最後に、(8)のようにfutureプロパティを返せば、Futureで扱うデータを変換して返すメソッドが作成できます。

作成したAPIクライアントを利用する

 先ほど作成したAPIクライアントを利用するようにしたILoginControllerを実装したものがリスト7です。これまでよりもかなりすっきりした実装が可能になります。

[リスト7]APIクライアントを使うための実装例(lib/client/ApiClient.dartの抜粋)
import 'package:chat_app/client/ApiClient.dart';
import 'package:chat_app/controller/ILoginController.dart';
import 'package:chat_app/data/Response.dart';
import 'package:http/http.dart' as http;

class ClientLoginController extends ILoginController{

  String apiKey;
  String baseUrl;
  ClientLoginController(this.apiKey,this.baseUrl);

  // : (省略)

  @override
  Future login(String loginId, String password){
    // (1) httpクライアントの作成
    var client = http.Client();
    try{
      var api = ApiClient(apiKey, baseUrl, client);
      return api.login(loginId, password);
    }
    finally{
      // (2) 切断
      client.close();
    }
  }
}

 (1)ではhttpのクライアントを作成します。このクラスはhttp接続を管理するためのクラスです。このインスタンスを共有すれば、一度の接続で複数のリクエストが行えます。ただし、(2)のように切断処理も自分で行う必要があります。

 このように必ず、一回の接続で、一回のリクエストしかしない場合には、このインスタンスを先ほどのApiClientクラス内で作成すればより処理が簡略化できるはずです。

最後に

 http通信自体は、httpパッケージを使えばそれほど難しくなく行えます。また、async/awaitキーワードを使えば、あまり非同期処理についてあまり意識する必要はないかもしれません。しかし、少々複雑なケースになってくると、非同期処理についてもう少し深いレベルでの理解が求められます。

 例えば、複数のhttpリクエストが必要な場合にasync/awaitキーワードだけで実装をしてしまうと、せっかくの非同期で待ち時間を節約できるメリットが活かせない場合があります。場合によっては、新たな非同期結果を作る必要があるため、今回のサンプル程度では少々、大げさですがCompleterを使って新たな非同期結果を作成する例などを紹介しました。

 また、サーバから取得したデータは端末側に保存するケースが多々あります。次回は、データを端末に保存する方法について紹介します。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Flutterで始めるモバイルアプリ開発連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/16336 2022/08/31 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング