VB10 / vexana

Vexana is network manager project with dio.
https://pub.dev/packages/vexana
MIT License
147 stars 42 forks source link

Bad state: The FormData has already been finalized. #104

Closed aligconnectinno closed 2 months ago

aligconnectinno commented 2 months ago

Null check fix on NetworkManagerErrorInterceptor

What is the current behavior?

onErrorWrapper calls multiple time when authentication failed. But if the data is FormData throws this error.

StateError (Bad state: The FormData has already been finalized. This typically means you are using the same FormData in repeated requests.)

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem

Model Classes

final class DataModel extends INetworkModel<DataModel>
    with IFormDataModel<DataModel> {
  DataModel({required this.id, required this.name});

  final int id;
  final String name;

  @override
  DataModel fromJson(Map<String, dynamic> json) {
    return DataModel(id: json['id'] as int, name: json['name'] as String);
  }

  @override
  Map<String, dynamic>? toJson() {
    return {
      'id': id,
      'name': name,
    };
  }
}

final class ResponseModel extends INetworkModel<ResponseModel> {
  ResponseModel({required this.code});

  final String code;
  @override
  ResponseModel fromJson(Map<String, dynamic> json) {
    return ResponseModel(code: json['code'] as String); // Temporary code.
  }

  @override
  Map<String, dynamic>? toJson() {
    return {
      'code': code,
    };
  }
}

Service and managers

final class TempNetworkManager extends NetworkManager<EmptyModel> {
  TempNetworkManager()
      : super(
          options: BaseOptions(baseUrl: 'baseUrl'),
          onRefreshToken: (exception, _) async {
            final idToken = _getToken();
            exception.requestOptions.headers['Authorization'] = idToken;
            return exception;
          },
        );

  static String _getToken() => 'Token';
}

final class TempService {
  TempService({required TempNetworkManager networkManager})
      : _networkManager = networkManager;

  final TempNetworkManager _networkManager;

  Future<void> fetchData() async {
    // Throws unauthorized exception.
    final response = await _networkManager.send<ResponseModel, ResponseModel>(
      'path',
      parseModel: ResponseModel(code: 'code'),
      method: RequestType.POST,
      data: DataModel(id: 1, name: 'Ali'),
    );
  }
}

What is the expected behavior?

  1. Checks the error.requestOptions.data is FormData then clone the object.
  2. If error.requestOptions.data is not FormData return the same object.