I am trying to set up a client to my nestJs server. I generate a swagger documentation with nestJS/swagger and from there I use the swagger_dart_code_generator to generate the API code for my client. The Server works fine, I tested this with Postman. All request do as they should.
The problem is my client. When I call any generated API function it goes to the server as planned, the server builds its response and sends it back. BUT THEN my client function does not get the response and instead the request is sent to the server again in an endless loop. My original function never gets a response and just waits forever. I tested the behavior in windows, web and android. I never get an error, or an exception. Is there a configuration I am missing? What else could trigger this behavior?
Edit deleted since unrelated.
Here is the code I wrote:
//build.yaml
targets:
$default:
sources:
- "lib/**"
- "lib/swagger/**"
builders:
chopper_generator:
options:
header: "//Generated code"
swagger_dart_code_generator:
options:
input_folder: "lib/swagger/"
output_folder: "lib/swagger/models/"
ignore_headers: true
separate_models: true
//services.dart
import 'dart:async';
import 'package:get_it/get_it.dart';
import 'package:my_client/core/services/my_authenticator.dart';
import 'package:my_client/swagger/models/client_index.dart';
final getIt = GetIt.instance;
Future<void> setupServices() async {
getIt.registerSingleton<MyApi>(MyApi.create(
baseUrl: Uri.parse("http://localhost:3000"),
authenticator: getIt<MyAuthenticator>(),
));
}
//my_authenticator.dart
import 'dart:async';
import 'dart:io';
import 'package:chopper/chopper.dart';
class MyAuthenticator extends Authenticator{
String token = "";
@override
FutureOr<Request?> authenticate(
Request request,
Response response, [
Request? originalRequest,
]) async {
if (response.statusCode == HttpStatus.unauthorized) {
final String? newToken = await refreshToken();
if (newToken != null) {
return request.copyWith(headers: {
...request.headers,
HttpHeaders.authorizationHeader: newToken,
});
}
}
return request;
}
Future<String?> refreshToken() async {
return token;
}
}
//user_provoider.dart
import 'package:my_client/core/extensions/date_time_extension.dart';
import 'package:my_client/core/models/user_week.dart';
import 'package:my_client/swagger/models/my_api.swagger.dart';
class UserWeekProvider {
final MyApi _api;
UserWeekProvider(this._api);
Future<UserWeek?> getWorkWeek(DateTime? date) async {
final date = (date?? DateTime.now()).toIso8601String();
final response = await _api.userWeekGet(
date: date
);
if (response.body == null) return null;
return UserWeek.fromResponse(response.body!);
}
}
I found it. It is the Authenticator. The old one always returned a request, and so chopper tried again in an endless loop. Now I give back a null in any case other than an unauthorized. So it does not try again. The actually authenticate process is not implemented. But it works for now.
This change works now:
class MyAuthenticator extends Authenticator {
String token = "";
@override
FutureOr<Request?> authenticate(
Request request,
Response response, [
Request? originalRequest,
]) async {
if (response.statusCode == HttpStatus.unauthorized) {
String? newToken = await refreshToken();
final Map<String, String> updatedHeaders =
Map<String, String>.of(request.headers);
if (newToken != null) {
newToken = 'Bearer $newToken';
updatedHeaders.update('Authorization', (String _) => newToken!,
ifAbsent: () => newToken!);
return request.copyWith(headers: updatedHeaders);
}
}
return null;
}
Future<String?> refreshToken() async {
return token;
}
}