Retrofit

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,表明參數所在的位置

  • @Path 表示參數是在路徑裡
  • @Query 表示參數是在 Query parameter 裡
  • @Body 表示參數是物件或是 Map,這邊不可以是字串,否則執行時會有錯誤。
  • @Field 表示參數是在 Form 裡,這邊就可以用字串了
  • @Part 表示參數是 File

寫這些都是為了讓 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)); 

真的就省掉很多功夫。

comments powered by Disqus