公司黑客松打到一半紀錄一下,
因為覺得 GetXConnect 要切太多格式雷同的 providers 有點冗,
畢竟服務規模如果不大的情境下其實用一個 network service 頂著就好,
所以就決定用 dio 接請求了,相對簡單又跟 axios 87%像,真香。
FormData
、Map
、含有 MultipartFile
的 FormData
都在守備範圍中。onResponse
裡的 handler.next
的參數必須是 Response<dynamic>
- 跟以前使用 JS 的 axios 認知不同,就算你想直接回傳 response.data.data
編譯也不會讓你過,我參考了官方的範例做法是把 response.data
直接改掉傳回去,因為 data 是被 Response 是先定義的合法 attribute。xxxModel.fromJson
的 parsing - onResponse
時做請求的 parsing,判斷後端服務客製化的 code 合法後,對請求得到的資料做的 parsing。下面我們用 LoginModel 當範例:
安裝依賴到 pubspec.yaml
dependecies:
json_annotation: ^4.0.0
dev_dependencies:
build_runner: ^2.0.0
json_serializable: ^4.0.0
定義 LoginModel
,相對位置於lib/models/login_model.dart
。
import 'package:json_annotation/json_annotation.dart';
import 'package:my_app/models/token_model.dart';
part 'login_model.g.dart';
@JsonSerializable()
class LoginModel {
LoginModel({
required this.token,
required this.created,
});
/**
* 注意兩件事
* 1. 可以把用 JsonKey 把 data attribute 轉成駝峰式命名。
* 2. 當模型較深層,可以引用其他 model class。
*/
@JsonKey(name: 'token')
TokenModel token;
@JsonKey(name: 'created')
bool created;
factory LoginModel.fromJson(Map<String, dynamic> json) =>
_$LoginModelFromJson(json);
Map<String, dynamic> toJson() => _$LoginModelToJson(this);
}
執行 pub run build_runner build
生成 login_model.g.dart
在專案中快樂使用 LoginModel.fromJson
將 JSON 資料轉成 Class
之後想要新增任意 model 都可以依樣畫葫蘆,讚讚。
以下為 Network Service 的 Class 實作:
import 'package:dio/dio.dart';
import 'package:get_storage/get_storage.dart';
import 'package:my_app/models/login_model.dart';
import 'package:my_app/models/response_wrapper_model.dart';
Dio initNetworkServiceDio() {
var dio = Dio(
BaseOptions(
baseUrl: 'BASE_URL',
connectTimeout: 30000,
receiveTimeout: 30000,
responseType: ResponseType.json,
),
);
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) {
GetStorage box = GetStorage();
final accessToken = box.read('access_token');
/*
登入若有提供 token 的效期的話
可以在打請求前判斷效期是否已過做重 fetch 請求的事
不過目前公司黑客松沒提供 refresh token 的 API
*/
if (accessToken is String) {
options.headers['Authorization'] = 'Bearer $accessToken';
}
handler.next(options);
},
onResponse: (Response response, handler) {
// 請求的 parsing
final body = ResponseWrapperModel.fromJson(response.data);
if (body.code != 2000) {
return handler.reject(
DioError(
requestOptions: response.requestOptions,
response: response,
type: DioErrorType.other,
error: body.msg,
),
);
}
// 改寫 response.data
response.data = body.data;
return handler.next(response);
},
onError: (DioError e, handler) {
// invalid responseStatusCode 的錯誤處理在這邊
return handler.next(e);
},
),
);
return dio;
}
class NetworkService {
static final Dio _dio = initNetworkServiceDio();
// 註冊/登入
static Future<LoginModel> login(Map form) async {
final response = await _dio.post('login/', data: form);
// 請求得到的資料的 parsing
return LoginModel.fromJson(response.data);
}
// 取得SMS驗證碼
static Future<Response<dynamic>> smsValidation(Map form) =>
_dio.post('sms-verification/', data: form);
}
浪費了不少時間在踩坑上,希望以後要接請求的朋友們不會這麼痛苦。
如果有更好的做法也希望能交流一下。
題外話,用手機測 SMS 登入失敗了很多次,不小心名正言順地當了一回薪水小偷,
好險 PM 說沒事。
Flutter