dio 是 dart 一個很知名的 HTTP 客戶端函式庫,大抵就跟 Python 的 requests 一樣,非常好用。不過要刻出 API 函式庫,也是要做不少苦工。我是在 pub.dev 找 dio 時,意外看到 retrofit ,仔細看了他的使用說明,才 發現這很方便耶,如果之前有照 OpenAPI 規格定 API 文件的話,寫起來會蠻輕鬆的。retrofit 底層除了使用 dio 之外,還使用了 json_annotation / json_serializable 來輔助定義 request body 跟 response body, 這樣少寫很多程式。
以下都是在 Android Studio + flutter 做的。
首先,在 pubspec.yaml 裡寫 dependencies 跟 dev_dependencies
dependencies:
retrofit: any
logger: any #for logging purpose
dev_dependencies:
retrofit_generator: any
build_runner: any
json_serializable: ^3.3.0
然後在 lib 下新增 example.dart
// 內容主要依照 retrofit README.md 裡的範例來修改
import 'package:json_annotation/json_annotation.dart';
import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.dart';
// 這個會在執行 flutter pub run build_runner build 的時候,自動產生出來
part 'example.g.dart';
@RestApi(baseUrl: "https://example.com/api/v1")
abstract class RestClient {
factory RestClient(Dio dio, {String baseUrl}) = _RestClient;
@GET("/tasks")
Future<List<Task>> getTasks();
@GET("/tasks/{id}")
Future<Task> getTask(@Path("id") String id);
@PATCH("/tasks/{id}")
Future<Task> updateTaskPart(
@Path() String id, @Body() Map<String, dynamic> map);
@PUT("/tasks/{id}")
Future<Task> updateTask(@Path() String id, @Body() Task task);
@DELETE("/tasks/{id}")
Future<void> deleteTask(@Path() String id);
@POST("/tasks")
Future<Task> createTask(@Body() Task task);
}
// JsonSerializable 是表明要為 Task 產生生成JSON跟還原JSON為物件的函式。
@JsonSerializable()
class Task {
String id;
String name;
String createdAt;
Task({this.id, this.name, this.createdAt});
// _$TaskFromJson() 跟 _$TaskToJson() 這兩個函式是產生出來的,都在 example.g.dart 裡。
factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
Map<String, dynamic> toJson() => _$TaskToJson(this);
}
RestClient 類別就是定義 Resource 的 HTTP method,類別必須是 abstract。 函式的前面需要加上 @PUT / @GET / @PATCH / @DELETE / @POST 等 annotation 來表明這函式是處理哪個 HTTP METHOD。
參數則有 @Path, @Body, @Query 等 annotation,表明參數所在的位置
寫這些都是為了讓 retrofit 可以依照 annotation 以及類別的名稱產生出真正的函式實作。
Task 是主要的 Resource ,你會看到有 @JsonSerializable 這個 annotation,在使用
flutter pub run build_runner build
時,會依據這個來幫 Task 產生生成JSON跟還
原JSON為物件的函式。_$TaskFromJson() 跟 _$TaskToJson() 這兩個函式就是產生出來的
,產生出來的函式會放在 example.g.dart 裡。
那使用的時候怎麼使用呢?
final Dio dio = Dio();
RestClient restClient = RestClient(dio, baseUrl: "https://example.com/api/v1");
// 如果不帶 baseurl 的話,會自動使用寫在 @RestApi annotation 裡的 baseUrl
restClient.getTasks().then((it) => print(it));
restClient.updateTaskPart("1", {'name': 'foo'}).then((task) => print(task));
真的就省掉很多功夫。