📠
Flutter (Dart) の API Client
概要
DartでHTTP通信を担うAPI Client作ります。(自明)
Dartコアライブラリである http のみの利用。
可用性とテスタビリティを良好にしつつ、シンプルな構成を目指します。
実装
実際に書いたコードhttps://httpbin.org/ を利用させていただきました。便利なのでおすすめ。
今回は叩くAPIのモックとしてhttpライブラリのリファレンスみるとお分かりになると思いますが、get
やpost
が実行メソッドとして用意されています。ただ、利用すると手続的なコードを強いられるので使いません。リクエスト構造体(クラス)に情報集めてクライアントで実行するだけが好ましいです。
HTTP通信の実体部分(ライブラリになげる処理)
import 'package:http/http.dart' as http;
class ApiCore {
final http.Client _client = http.Client();
final Duration _timeLimit = const Duration(seconds: 30);
Future<http.Response> send(http.BaseRequest req) async {
return http.Response.fromStream(
await _client.send(req).timeout(_timeLimit));
}
}
自パッケージ側でリクエストを組みたてやレスポンスの共通処理
class ApiClient {
// 実務での利用では適切に依存関係を注入してください
final api = ApiCore();
Future<http.Response> execute(Request req) async {
final http.Request request = await _build(req);
try {
final response = await api.send(request);
if (200 <= response.statusCode && response.statusCode <= 299) {
return response;
} else {
throw _createException(response);
}
} catch (_) {
rethrow;
}
}
Future<http.BaseRequest> _build(Request req) async {
final Map<String, String> headers = await req.headers();
final Map<String, String> query = await req.query();
final Map<String, dynamic> body = await req.body();
Uri url = Uri.https(req.domain, req.path, query);
return http.Request(req.method.name.toUpperCase(), url)
..headers.addAll(headers)
..body = json.encode(body);
}
Exception _createException(http.Response res) {
// 実務での利用では適切にアプリ定義Exceptionを発生させる
return Exception({res.body});
}
}
自パッケージ内で定義しているRequestクラスの例
abstract class Request {
String domain = Constant.domain;
abstract String path;
abstract HTTPMethod method;
Future<Map<String, String>> headers() async {
return {
'Accept': 'application/json',
'Content-Type': 'application/json',
};
}
Future<Map<String, String>> query() async => {};
Future<Map<String, dynamic>> body() async => {};
}
実際にリクエストクラスを実装する例
class SampleGet extends Request {
HTTPMethod method = HTTPMethod.get;
String path = '/get';
Future<Map<String, String>> query() async {
return {
'test': 'query',
};
}
}
最後に
今回のサンプルコードだとApiClientにApiCoreをがっつり入れ込んでいますが、ApiClient生成時に投げ込むなりProvider使うなりで、ApiCoreへの依存を切り分けてあげるとよいです。APIのモック化が捗ると思います。
返却されたResponseをどう処理するかはお好みです。
asyncライブラリにResult型があるので、利用することで、Swift的な非同期ハンドリングっぽくすることもできます。ただし、クロージャで処理が階層的になっていく点は一長一短もあります。
Dartの try-catch は非常に優秀で、awaitしている非同期処理のハンドリングもできます。
メリット・デメリットを考えてから、プロジェクトに合わせた形で処理しましょう。
ここらへんのとても参考になる記事
Discussion