Flutterに限らずですが、retrofitを用いたAPI通信をする場合にログを出力しながら
デバッグしていきたい場面があると思います。
今回はサンプルとしてホットペッパーグルメAPIを用いながら解説していきます。
ちなみに、retrofitは以下のようにAPI通信に関わるデータの変換処理を簡易的にしてくれるパッケージです。
build_runner
を実行すると、.g.dart
ファイルが生成され、めんどくさい変換処理を担ってくれます。
あくまで変換を簡易的にしてくれるもので、実際に通信を簡易的にしてくれるのはdio
パッケージの役割です。
@RestApi()
abstract class HotpepperApiClient {
factory HotpepperApiClient(AppDio dio, {String? baseUrl}) =
_HotpepperApiClient;
@GET('')
Future<String> fetchHotpepperInfo({
@Query('key') required String apiKey,
@Query('format') required String format,
@Query('lat') required double latitude,
@Query('lng') required double longitude,
@Query('range') required int range,
});
}
(今回はAppDio
がメインテーマになるので後ほど解説します)
使い方はこんな感じです。
final hotpepperClient = HotpepperApiClient(AppDio());
await hotpepperClient.fetchHotpepperInfo(
apiKey: 'xxxxxx',
format: 'json',
latitude: 35.351532,
longitude: 139.845848,
range: 1,
);
このretrofit / dioはとても便利なのですが、実際にAPI通信のリクエストとレスポンスでどう言うデータを送受信しているかは設定しないと確認できません。以下で提示するようにDioMixin
を継承してカスタムすることが可能です。
import 'package:dio/dio.dart';
class AppDio extends DioMixin {
factory AppDio() {
final instance = _instance;
if (instance != null) {
return instance;
}
final dio = AppDio._();
final options = BaseOptions(
baseUrl: 'https://webservice.recruit.co.jp/hotpepper/gourmet/v1',
connectTimeout: const Duration(seconds: 15),
headers: {
'accept': '*/*',
'Accept-Language': 'ja',
'X-Api-Version': '2.0',
'Content-Type': 'application/json',
},
contentType: 'application/json',
);
dio
..options = options
..httpClientAdapter = HttpClientAdapter();
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
print('######## Request Log ########');
print('--> ${options.method} ${options.path}');
print('Full URL: ${options.uri}');
print('Headers: ${options.headers}');
print('Query Parameters: ${options.queryParameters}');
print('Request Data: ${options.data}');
return handler.next(options);
},
onResponse: (Response response, ResponseInterceptorHandler handler) {
print('######## Response Log ########');
print('<-- ${response.statusCode} ${response.requestOptions.path}');
print('Headers: ${response.headers}');
print('Response Data: ${response.data}');
return handler.next(response);
},
onError: (DioException e, ErrorInterceptorHandler handler) {
print('######## Error Log ########');
print('<-- Error --> ${e.message}');
return handler.next(e);
},
),
);
_instance = dio;
return dio;
}
AppDio._();
static AppDio? _instance;
}
それを、冒頭のコードと同じになってしまいますがretrofitのコードに指定すればOKです
@RestApi()
abstract class HotpepperApiClient {
factory HotpepperApiClient(AppDio dio, {String? baseUrl}) =
_HotpepperApiClient;
@GET('')
Future<String> fetchHotpepperInfo({
@Query('key') required String apiKey,
@Query('format') required String format,
@Query('lat') required double latitude,
@Query('lng') required double longitude,
@Query('range') required int range,
});
}
サンプルなのでprint関数を用いていますが、おそらくlintで警告が出てしまうと思います。
そのため、適宜以下のようなログ用のパッケージを導入して置き換えてみてください。
ただし、Dioパッケージは便利なものでデフォルトでもLogInterceptor
というクラスが用意されているためこちらを用いてもいいですね。
手動でログをカスタムする書き方を提示しまいしたが、ある程度のフォーマットであれば以下のような記述でも問題ないと思います。
dio.interceptors.add(
- InterceptorsWrapper(
- onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
- debugPrint('######## Request Log ########');
- debugPrint('--> ${options.method} ${options.path}');
- debugPrint('Full URL: ${options.uri}');
- debugPrint('Headers: ${options.headers}');
- debugPrint('Query Parameters: ${options.queryParameters}');
- debugPrint('Request Data: ${options.data}');
- return handler.next(options);
- },
- onResponse:
- (Response<dynamic> response, ResponseInterceptorHandler handler) {
- debugPrint('######## Response Log ########');
- debugPrint(
- '<-- ${response.statusCode} ${response.requestOptions.path}',
- );
- debugPrint('Headers: ${response.headers}');
- debugPrint('Response Data: ${response.data}');
- return handler.next(response);
- },
- onError: (DioException e, ErrorInterceptorHandler handler) {
- debugPrint('######## Error Log ########');
- debugPrint('<-- Error --> ${e.message}');
- return handler.next(e);
- },
- ),
- );
+ dio.interceptors.add(LogInterceptor(
+ request: true,
+ requestHeader: true,
+ requestBody: true,
+ responseHeader: true,
+ responseBody: true,
+ ));