Integrate Interceptor into an asynchronous operation, inspired by Dio.
Motivation
Enables the breakdown of tasks into smaller, independent operational units (Interceptor).
Easily adds new functionality to the application by creating new Interceptors.
Easy to modify and upgrade as Interceptors are not dependent on each other.
Increases code reuse.
Auto catch all exception.
Features
onRequest
: allows monitoring and modification of the input request.onResponse
: allows monitoring and modification of the response data.onError
: allows monitoring and modification of the error.onTransform
: allow modification of the FutureResponse before returning to the caller.extra
: Custom field that can be retrieved later.- Extensions: a way to add a specific feature to an Interceptor
Usage
/// Create your Interceptor
class LoggingInterceptor extends Interceptor {
@override
InterceptorRequestCallback? get onRequest => (options) {
print("onRequest");
return options;
};
@override
InterceptorDataCallback? get onResponse => (options, data) {
print("onResponse with data $data");
return data;
};
@override
InterceptorErrorCallback? get onError => (options, error) {
print("onError $error");
return error;
};
@override
InterceptorTransformCallback? get onTransform => (response) {
print("onTransform response $response");
return response;
};
}
/// Create a class that setup interceptors and expose fetch method
class MyClass {
late FutureInterceptor _futureInterceptor;
MyClass() {
_futureInterceptor = FutureInterceptor();
_futureInterceptor.interceptors.add(LoggingInterceptor());
}
Future<FutureResponse<dynamic>> fetch(FetchMethod request) async {
return _futureInterceptor.fetch(FutureRequestOptions(
request: request,
extra: {'key' : 'val'},
));
}
}
/// Finally
Future test() async {
final myClass = MyClass();
final res = await myClass.fetch(() async {
// Do your work here
return "my result";
});
if (res.error != null) {
print("Error: ${res.error}");
} else {
print("Response: ${res.data}");
}
}
Some rules
- FutureInterceptor.fetch() will never throw an exception.
- The operation of each Interceptor is not affected by other Interceptors. However, the Interceptor's input can be changed by the Interceptor before it.
onRequest
andonTransform
callbacks will always be called.- If request return a result, all
onResponse
callbacks will be called. - If request throws an exception, all
onError
callbacks will be called. - If a callback throws an exception, it will be ignored.
Extensions
RecordExtension
RecordExtension is used to store an unique data for each request.
Unlike FutureRequestOptions, a record cannot be modified by other Interceptors.
A record is not shared between requests.
Example:
class InterceptorWithRecord extends Interceptor with RecordExtension {
int id = 0;
@override
InterceptorRequestCallback get onRequest => (option) {
setRecord(id++);
return option;
};
@override
InterceptorDataCallback get onResponse => (option, data) {
/// Show the data that has been set by the previous callback of this request.
print('Record = $record');
return data;
};
}