fetch<T> method
The eventual method to submit requests. All callers for requests should eventually go through this method.
Implementation
@override
Future<Response<T>> fetch<T>(RequestOptions requestOptions) async {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
// Convert the request interceptor to a functional callback in which
// we can handle the return value of interceptor callback.
FutureOr Function(dynamic) requestInterceptorWrapper(
InterceptorSendCallback cb,
) {
return (dynamic incomingState) {
final state = incomingState as InterceptorState;
if (state.type == InterceptorResultType.next) {
return listenCancelForAsyncTask(
requestOptions.cancelToken,
Future(() async {
final handler = RequestInterceptorHandler();
cb(state.data as RequestOptions, handler);
return handler.future;
}),
);
}
return state;
};
}
// Convert the response interceptor to a functional callback in which
// we can handle the return value of interceptor callback.
FutureOr<dynamic> Function(dynamic) responseInterceptorWrapper(
InterceptorSuccessCallback cb,
) {
return (dynamic incomingState) {
final state = incomingState as InterceptorState;
if (state.type == InterceptorResultType.next ||
state.type == InterceptorResultType.resolveCallFollowing) {
return listenCancelForAsyncTask(
requestOptions.cancelToken,
Future(() async {
final handler = ResponseInterceptorHandler();
cb(state.data as Response, handler);
return handler.future;
}),
);
}
return state;
};
}
// Convert the error interceptor to a functional callback in which
// we can handle the return value of interceptor callback.
FutureOr<dynamic> Function(Object) errorInterceptorWrapper(
InterceptorErrorCallback cb,
) {
return (dynamic error) {
final state = error is InterceptorState
? error
: InterceptorState(assureDioException(error, requestOptions));
Future<InterceptorState> handleError() async {
final handler = ErrorInterceptorHandler();
cb(state.data, handler);
return handler.future;
}
// The request has already been cancelled,
// there is no need to listen for another cancellation.
if (state.data is DioException &&
state.data.type == DioExceptionType.cancel) {
return handleError();
}
if (state.type == InterceptorResultType.next ||
state.type == InterceptorResultType.rejectCallFollowing) {
return listenCancelForAsyncTask(
requestOptions.cancelToken,
Future(handleError),
);
}
throw error;
};
}
// Build a request flow in which the processors(interceptors)
// execute in FIFO order.
Future<dynamic> future = Future<dynamic>(
() => InterceptorState(requestOptions),
);
// Add request interceptors into the request flow.
for (final interceptor in interceptors) {
final fun = interceptor is QueuedInterceptor
? interceptor._handleRequest
: interceptor.onRequest;
future = future.then(requestInterceptorWrapper(fun));
}
// Add dispatching callback into the request flow.
future = future.then(
requestInterceptorWrapper((
RequestOptions reqOpt,
RequestInterceptorHandler handler,
) async {
requestOptions = reqOpt;
try {
final value = await _dispatchRequest<T>(reqOpt);
handler.resolve(value, true);
} on DioException catch (e) {
handler.reject(e, true);
}
}),
);
// Add response interceptors into the request flow
for (final interceptor in interceptors) {
final fun = interceptor is QueuedInterceptor
? interceptor._handleResponse
: interceptor.onResponse;
future = future.then(responseInterceptorWrapper(fun));
}
// Add error handlers into the request flow.
for (final interceptor in interceptors) {
final fun = interceptor is QueuedInterceptor
? interceptor._handleError
: interceptor.onError;
future = future.catchError(errorInterceptorWrapper(fun));
}
// Normalize errors, converts errors to [DioException].
try {
final data = await future;
return assureResponse<T>(
data is InterceptorState ? data.data : data,
requestOptions,
);
} catch (e) {
final isState = e is InterceptorState;
if (isState) {
if (e.type == InterceptorResultType.resolve) {
return assureResponse<T>(e.data, requestOptions);
}
}
throw assureDioException(isState ? e.data : e, requestOptions);
}
}