Chapter 20

  API通信の実装

heyhey1028
heyhey1028
2023.02.27に更新

この章では Qiita API をアクセストークンを使って呼び出し、返されたデータをモデルクラスに変換する処理を実装していきます。

アクセストークンの秘匿化

Qiita API を呼び出すにあたって、先に作成したアクセストークンをリクエストに含める必要があります。

コード上にベタ打ちでアクセストークンを記述することもできますが、それでは Github などにコードを公開した際にアクセストークンが漏れてしまいます。

そこで、今回はこういったアクセストークンを秘匿化するための、flutter_dotenv というパッケージを使ってみましょう。

https://pub.dev/packages/flutter_dotenv

1. パッケージのインストール

まずは、flutter_dotenv をインストールします。

$ flutter pub add flutter_dotenv

2. .env ファイルの作成

次に、flutter_dotenv が読み込む .env ファイルを作成します。

ルートディレクトリの直下に .env ファイルを作成し、以下のようにアクセストークンを記述します。

QIITA_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

xxxx となっている部分に先ほど作成したアクセストークンを記述してください。

3. .gitignore.env を追加

.envファイルが github にプッシュされてしまうとアクセストークンが公開されてしまうので.gitignore.env ファイルを追加します。

# .env ファイルを追加
.env

4. assets として.envを登録

次に、pubspec.yaml.env ファイルを assets として登録します。

flutter:
  uses-material-design: true
  assets:
    - .env # <- ここに追記

5. .env ファイルの読み込み

最後に、main.dart.env ファイルを読み込みます。

import 'package:flutter_dotenv/flutter_dotenv.dart';

Future<void> main() async {
  await dotenv.load(fileName: '.env');
  runApp(MyApp());
}

これで、dotenv で読み込んだアクセストークンは、どこのファイルからもdotenv.env['QIITA_ACCESS_TOKEN'] で取得できるようになります。

final String token = dotenv.env['QIITA_ACCESS_TOKEN']); // .env に記述したアクセストークンを取得

これで準備は整いました。

API 通信の実装

それでは Qiita API を呼び出す非同期関数searchQiita()を実装していきましょう。

今回は先に作ったsearch_screen.dart内で処理を呼び出すので、search_screen.dart にメソッドを追加していきます。

1. 使用するパッケージのインポート

まずは、使用するパッケージをファイルをインストールし、インポートしておきましょう。

flutter pub add http

json 変換で使うconvert、http 通信で使うhttp、秘匿化したアクセストークンを取得する為のflutter_dotenv、そして作成したモデルクラスarticle.dartをインポートします。

import 'dart:convert';
+ import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:qiita_app/models/article.dart';

2. 関数の定義

次に中の処理は置いておいて、関数の定義だけしておきましょう。

Future<List<Article>> searchQiita(String keyword) async {
    // 処理を実装
}

API を呼び出し、その結果を待つ非同期処理になるのでFuture、そして受け取った結果は複数のArticleクラスに変換するのでリストに格納し、List<Article>型にするので、関数の返り値は Future<List<Article>> 型になります。

また検索キーワードをリクエストに含める為、String型の引数を受け取ります。

非同期関数の為、asyncも忘れずに。

3. Uri の作成

次に、API のエンドポイントを指定するためのUriを作成します。

今回はクエリを含める為、先に紹介したUri.parse()ではなく、クエリを記述しやすいUri.https()を使います。

Uri.httpsでは下記の通り、第一引数に baseUrl、第二引数に path、第三引数にMap<String,dynamic>型でクエリパラメータを指定します。

final url = Uri.https('qiita.com', '/api/v2/items', {
    'query': 'title:$keyword',
    'per_page': '10',
});

タイトルで検索をかける為、queryフィールドにtitleを指定。記事を 10 件取得する為、per_pageフィールドに10を指定しています。

4. リクエストの作成

肝となるリクエストの作成です。

dotenv を使ってアクセストークンを取得し、リクエストヘッダーに含めて GET リクエストを送信します。

awaitで非同期処理を待ち、http.Response型のresに結果を格納します。

// アクセストークンを取得
final String token = dotenv.env['QIITA_ACCESS_TOKEN'] ?? '';

// アクセストークンを含めてリクエストを送信
final http.Response res = await http.get(uri, headers: {
    'Authorization': 'Bearer $token',
});

5. ステータスに応じた処理

レスポンスを受け取っても通信が成功したとは限りません。http.Responseに含まれるステータスコードを使って、処理を分岐させましょう。

ステータスコードが 200 で成功した場合のみ、結果をモデルクラスへ変換し、それ以外の場合は空のリストを返します。

    if (res.statusCode == 200) {
      // モデルクラスへ変換
    } else {
      return [];
    }

6. モデルクラスへの変換

成功した API 通信のレスポンスをモデルクラスへ変換します。

レスポンスは複数の投稿データの配列なので、一度jsonDecode()を使ってList<dynamic>型に変換します。

その後、map()List<dynamic>型の配列の中身を1つ1つ factory コンストラクタを使ってArticleオブジェクトに変換し、toList()で再度配列に格納し直し返します。

// レスポンスをモデルクラスへ変換
final List<dynamic> body = jsonDecode(res.body);
return body.map((dynamic json) => Article.fromJson(json)).toList();

以上で完成です 🙌

全体のコード
search_screen.dart
+ import 'dart:convert';
+ import 'package:http/http.dart' as http;
+ import 'package:flutter_dotenv/flutter_dotenv.dart';

import 'package:flutter/material.dart';
+ import 'package:qiita_search/models/article.dart';

class SearchScreen extends StatefulWidget {
  const SearchScreen({super.key});

  
  State<SearchScreen> createState() => _SearchScreenState();
}

class _SearchScreenState extends State<SearchScreen> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Qiita Search'),
      ),
      body: Container(),
    );
  }

+  Future<List<Article>> searchQiita(String keyword) async {
+    final uri = Uri.https('qiita.com', '/api/v2/items', {
+      'query': 'title:$keyword',
+      'per_page': '10',
+    });
+
+    final String token = dotenv.env['QIITA_ACCESS_TOKEN'] ?? '';
+    final http.Response res = await http.get(uri, headers: {
+      'Authorization': 'Bearer $token',
+    });
+
+    if (res.statusCode == 200) {
+      // レスポンスをモデルクラスへ変換
+      final List<dynamic> body = jsonDecode(res.body);
+      return body.map((dynamic json) => Article.fromJson(json)).toList();
+    } else {
+      return [];
+    }
+  }
}

まとめ

以上でようやく Qiita API から欲しいデータを取得する処理を実装する事が出来ました。

次章からついに Widget を使った UI 実装に入っていきます。