I'm using clean architecture on a project and using dio as http package and I've added pretty DioLogger as a dio inceptor to log requests and response.
the pretty dio logger is logging a reponse of 200 ok but I'm getting instead of a correct dio response I'm getting dioErrorType.unknown and I'm unable to reach the actual response, what could be the cause of this error?
I've made a test project with only the faulty code, here's the dio factory class:
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
class DioFactory {
Dio getDio() {
Dio dio = Dio();
Map<String, String> headers = {
'content-type': 'application/json',
'accept': 'application/json',
};
dio.options = BaseOptions(
baseUrl: 'https://1rkj9.wiremockapi.cloud/',
headers: headers,
sendTimeout: const Duration(milliseconds: 60 * 1000),
receiveTimeout: const Duration(milliseconds: 60 * 1000),
);
if (!kReleaseMode) {
dio.interceptors.add(
PrettyDioLogger(
requestHeader: true,
requestBody: true,
responseHeader: true,
),
);
}
return dio;
}
}
I've used retrofit generator to generate the request, here is my app client class:
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
import '../responses/responses.dart';
part 'app_api.g.dart';
@RestApi(baseUrl: 'https://1rkj9.wiremockapi.cloud/')
abstract class AppServiceClient {
factory AppServiceClient({required Dio dio, String? baseURL}) =>
_AppServiceClient(dio);
@POST('/customer/forgotPassword')
Future<ForgotPasswordResponse> forgotPassword(@Field('email') String email);
}
and here's the generated app:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'app_api.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers
class _AppServiceClient implements AppServiceClient {
_AppServiceClient(
this._dio, {
this.baseUrl,
}) {
baseUrl ??= 'https://1rkj9.wiremockapi.cloud/';
}
final Dio _dio;
String? baseUrl;
@override
Future<ForgotPasswordResponse> forgotPassword(String email) async {
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = {'email': email};
final _result = await _dio.fetch<Map<String, dynamic>>(
_setStreamType<ForgotPasswordResponse>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/customer/forgotPassword',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl)));
final value = ForgotPasswordResponse.fromJson(_result.data!);
return value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
}
I've used json_annotation package to generate the repsonses, here's the response file:
import 'package:json_annotation/json_annotation.dart';
part 'responses.g.dart';
@JsonSerializable()
class BaseResponse {
@JsonKey(name: "status")
int? status;
@JsonKey(name: "message")
String? message;
}
@JsonSerializable()
class ForgotPasswordResponse extends BaseResponse {
@JsonKey(name: "support")
String? support;
ForgotPasswordResponse({required this.support});
factory ForgotPasswordResponse.fromJson(Map<String, dynamic> json) =>
_$ForgotPasswordResponseFromJson(json);
Map<String, dynamic> toJson() => _$ForgotPasswordResponseToJson(this);
}
and here's the generated response file:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'responses.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
BaseResponse _$BaseResponseFromJson(Map<String, dynamic> json) => BaseResponse()
..status = json['status'] as int?
..message = json['message'] as String?;
Map<String, dynamic> _$BaseResponseToJson(BaseResponse instance) =>
<String, dynamic>{
'status': instance.status,
'message': instance.message,
};
ForgotPasswordResponse _$ForgotPasswordResponseFromJson(
Map<String, dynamic> json) =>
ForgotPasswordResponse(
support: json['support'] as String?,
)
..status = json['status'] as int?
..message = json['message'] as String?;
Map<String, dynamic> _$ForgotPasswordResponseToJson(
ForgotPasswordResponse instance) =>
<String, dynamic>{
'status': instance.status,
'message': instance.message,
'support': instance.support,
};
and finally this is the main file:
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:testing_app/network/app_api.dart';
import 'network/dio_factory.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final AppServiceClient appServiceClient =
AppServiceClient(dio: DioFactory().getDio());
MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('test app'),
),
body: Center(
child: TextButton(
onPressed: () async {
try {
final response =
await appServiceClient.forgotPassword('abc@gmail.com');
log('response code: ${response.status}');
} catch (error) {
if (error is DioError) {
log('error type: ${error.type}');
}
}
},
child: Text('start dio post request'),
),
)),
);
}
}
The problem was that I didn't add "Content-Type":"application/json" in the header of the response in the mockAPI that I'm using