本系列同步發表在 個人部落格,歡迎大家關注~
唷西(よし),昨天花了很大的力氣將 JSON 序列化的物件模板建立完成了。
那麼下一步就是要整合 API 拉~
lib/services/
底下新增 github_trending_api.dart
。github_trending_api.dart
內會實現 class GitHubTrendingApiClient
,其目的方便頁面直接創建 client 物件並透過方法調用 API。接下來,先了解程式碼架構。
lib/services/github_trending_api.dart
import 'dart:convert';
import 'package:gitme_reborn/services/models/developer.dart';
import 'package:http/http.dart' as http;
import 'package:gitme_reborn/services/models/project.dart';
class GitHubTrendingApiError extends Error {
final String message;
GitHubTrendingApiError(this.message);
@override
String toString() {
return "GitHubTrendingApiError(message: $message)";
}
}
class GitHubTrendingApiClient {
http.Client _client;
String baseDomain = "github-trending-api.now.sh";
GitHubTrendingApiClient({client}) {
this._client = client != null ? client : http.Client();
}
Uri get baseUrl => Uri.https(baseDomain, "");
Future<List<Project>> listProjects(...) async {...}
Future<List<Developer>> listDevelopers(...) async {...}
}
從上可以觀察到
GitHubTrendingApiError
用來 handle 調用 API 時發生錯誤。GitHubTrendingApiClient
包含以下這些部份
_client
、 baseDomain
GitHubTrendingApiClient({client})
baseUrl
listProjects(...)
、 listDevelopers(...)
小提醒:
- 類別或物件中的變數通常稱作「屬性」(property),而函數通常稱作「方法」(method)
_client
是http.Client
類別,可以直接用get(...)
來傳送 GET 請求;post(...)
傳送 POST 請求。http
是 Dart 語言中傳送 HTTP(s) 請求的核心函數庫,很多功能它都包好了,非常簡單易用。
前一節,listProjects(...)
和 listDevelopers(...)
這兩個方法被我省略了。
這一節仔細展開實做吧!
Future<List<Project>> listProjects({String language, String since}) async {
var requestUrl = Uri.https(baseDomain, "/repositories", {
if (language != null) "language": language,
if (since != null) "since": since,
});
final resp = await _client.get(requestUrl);
if (resp.statusCode != 200) {
throw GitHubTrendingApiError(resp.body);
}
return jsonDecode(resp.body)
.map<Project>((project) => Project.fromJson(project))
.toList();
}
Future<List<Developer>> listDevelopers({String language, String since}) async {
var requestUrl = Uri.https(baseDomain, "/developers", {
if (language != null) "language": language,
if (since != null) "since": since,
});
final resp = await _client.get(requestUrl);
if (resp.statusCode != 200) {
throw GitHubTrendingApiError(resp.body);
}
return jsonDecode(resp.body)
.map<Developer>((developer) => Developer.fromJson(developer))
.toList();
}
其實這兩個方法大同小異,分成五部份:
{String language, String since}
: 搭配 API Request 得 GET 參數。requestUrl
: 用 Uri.https
這 Factory Constructor,帶入對應的參數 authority
、unencodedPath
、queryParameters
。_client
送 GET Request 到 requestUrl
。GitHubTrendingApiError
處理 Response 狀態碼不是 200 的情況。jsonDecode
轉換 resp.body
成 List<Map>
,最後再由 .fromJson()
轉換成 List<Project>
或 List<Developer>
既然是自己寫的輪子,就需要簡單測試一下,看能不能成功獲取到資料。
test/services/github_trending_api_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:gitme_reborn/services/github_trending_api.dart';
import 'package:gitme_reborn/services/models/project.dart';
import 'package:gitme_reborn/services/models/developer.dart';
main() {
group('Test GitHub trending APIs', () {
test('Test method listProjects successfully', () async {
final api = GitHubTrendingApiClient();
expect(await api.listProjects(), isInstanceOf<List<Project>>());
});
test('Test method listDevelopers successfully', () async {
final api = GitHubTrendingApiClient();
expect(await api.listDevelopers(), isInstanceOf<List<Developer>>());
});
});
}
先建立一個非常簡易的測試,我主要是要測試 listProjects
和 listDevelopers
這兩個方法能不能 Work 而已。
接著在 VSCode 裡,在測試 function 上會出現 Run|Debug
點擊 Run
就可以輕鬆跑測試囉~
看到綠色就代表測試通過~~ !!!
今天就先到這,明天能安心地使用自己建造的輪子拉~