ローソンデジタルイノベーション テックブログ

ローソンデジタルイノベーション(LDI)の技術ブログです

FlutterとDartを使ったAPI通信の実装方法

こんにちは、LDIのモバイルアプリエンジニアの庄司です。

前回に続き、在宅勤務報告システムの開発にまつわる話をしようと思います。
在宅勤務報告システムではhttpパッケージを使って、APIの呼び出しを実装しています。
今回はAPIへのリクエストとレスポンスの実装について書いていきます。

今回利用している関連ライブラリのバージョンは次のとおりです。

Flutter 3.7.7
Dart 2.19.2
http 0.13.5

APIへのリクエストとレスポンス

APIへのリクエスト

在宅勤務報告システムのAPIではPOSTメソッドでパラメータをJSONで受け取る仕様となっているので、 送信するデータをJSONにする必要がありました。

JSONへのエンコードについて

JSONへのエンコードはDartのコアライブラリの一つdart:convertで提供されていたので今回の開発ではこちらを使いました。

json.encode() に任意のデータをJSONのキー:値に対応したMapにして渡すとJSON文字列を得ることができます。

在宅勤務報告システムでは、Mapへの変換メソッドを持ったリクエストパラメータを持つクラスを定義してリクエストパラメータのJSONを生成できるようにしました。 下記は在宅勤務報告システムから一部を抜粋した実装例です。

class RequestGet {
  final List<String> userIds;
  final String idToken;
  final String? status;
  final String? targetMonth;
  final List<String> staffNumbers;

  RequestGet(
      {required this.userIds,
      required this.idToken,
      this.status,
      this.targetMonth,
      required this.staffNumbers});

  Map<String, dynamic> toJson() => {
        'userIds': userIds,
        'idToken': idToken,
        'status': status,
        'targetMonth': targetMonth,
        'staffNumbers': staffNumbers
      };
}

このクラスとjson.encode()を組み合わせると下記のようにJSON文字列を得られます。

var jsonEncodeText = json.encode(requestGet.toJson())
// ↓のようなJSON文字列に変換される
//
// { 
//    "userIds" : [userIds[0],userIds[1], ... ],
//    "idToken": idToken,
//    "status": status,
//    ''targetMonth": targetMonth,
//    "staffNumbers": [staffNumbers[0],staffNumbers[1], ... ]
// }
APIへのリクエストの実施

このJSONを使って、POSTリクエストを実施します。 下記は在宅勤務報告システムでのPOSTリクエストの処理の実装例です。 http.postの詳しい使い方はAPI referenceを参照してください。

  import 'package:http/http.dart' as http;

  // jsonEncodeTextでJSON文字列を受け取る
  Future<Response> _apiCall({url, jsonEncodeText}) {
    return http.post(url,
        body: {"data": jsonEncodeText},
        encoding: Encoding.getByName('utf-8'),
        headers: {
          "Content-Type": "application/json"
        }); 
  }

APIからのレスポンス

レスポンスのJSONのデコード

レスポンスの値はhttp.postの戻り値Responseで得られます。 bodyの値をResponse.bodyで文字列で受け取ることができるのでこれをアプリで定義するクラスに変換して扱っています。

レスポンスの値はJSONで得られるのでMap型のオブジェクトにデコードし、デコードしたオブジェクトから値を取り出してアプリ定義のクラスを作ります。

JSONからのデコードもdart:convertを利用します。
json.decode()を使うことでMap型に変換することができます。

在宅勤務報告システムでは、このMapからアプリ内データへの変換メソッドを持ったクラスを定義してレスポンスのデータを管理しています。 下記は在宅勤務報告システムでのレスポンスのデータを管理する実装例です。

class ResponseGet {
  final bool success;
  final String? errorMessage;
  final List<TeleworkPlan> items;

  ResponseGet(this.success, this.errorMessage, this.items);

  // JSONからObjectへの変換
  // jsonにはjson.decode(response.body)の戻り値を渡す
  factory ResponseGet.fromJson(Map<String, dynamic> json) {
    final list = json['list'];
    var items = List<TeleworkPlan>.empty();
    if (list != null) {
      items = list
          .map<TeleworkPlan>((item) => getTeleworkPlansFromJson(item))
          .toList();
    }
    ResponseGet res = ResponseGet(json['success'], json['errorMessage'], items);
    return res;
  }
  @protected
  static TeleworkPlan getTeleworkPlansFromJson(Map<String, dynamic> data) {

    // JSONのキーと値に対応したMapになっているので該当するキー名を指定すれば値を取り出せる
    return TeleworkPlan(
        userId: data['userId'],
        firstName: data['firstName'],
        lastName: data['lastName'],
        staffNumber: data['staffNumber'],
      // 中略
        reviewerStaffNumber: data['reviewerStaffNumber']);
  }
}

在宅勤務報告システムでの実装例

上記を組み合わせて実装したのが下記となります。

  Future<List<TeleworkPlan>> get(
      List<String> userIds,
      TeleworkPlanStatus? status,
      String? targetMonth,
      List<String> staffNumbers) async {
    var url = Uri.parse("ここにAPIのURLが入る");
    final idToken = await FirebaseAuth.instance.currentUser?.getIdToken(true);
    if (idToken == null) {
      throw Exception('idToken is null');
    }

    if (status == TeleworkPlanStatus.unspecified) {
      status = null;
    }
    var request = RequestGet(
        userIds: userIds,
        idToken: idToken,
        status: status?.name,
        targetMonth: targetMonth,
        staffNumbers: staffNumbers);

    // 非同期処理となるので注意
    final response =
        await _apiCall(url: url, jsonEncodeText: json.encode(request.toJson()));

    if (response.statusCode == 200) {
      var result = ResponseGet.fromJson(json.decode(response.body));
      if (!result.success) throw Exception(result.errorMessage);
      return result.items;
    } else {
      throw Exception('statusCode=${response.statusCode}');
    }
  }

最後に

少し基礎的な内容だったかもしれませんが、参考になれば幸いです。

今後も記事を更新していくので、「読者になる」で応援してください!