Skip to content

Commit

Permalink
fix: update docs and improve multipart support
Browse files Browse the repository at this point in the history
  • Loading branch information
CodingAleCR committed Jul 11, 2024
1 parent 5fadc05 commit 1bcdcc0
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 53 deletions.
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,26 @@ import 'package:http_interceptor/http_interceptor.dart';

In order to implement `http_interceptor` you need to implement the `InterceptorContract` and create your own interceptor. This abstract class has two methods: `interceptRequest`, which triggers before the http request is called; and `interceptResponse`, which triggers after the request is called, it has a response attached to it which the corresponding to said request. You could use this to do logging, adding headers, error handling, or many other cool stuff. It is important to note that after you proccess the request/response objects you need to return them so that `http` can continue the execute.

`interceptRequest` and `interceptResponse` use `FutureOr` syntax, which makes it easier to support both synchronous and asynchronous behaviors.

- Logging with interceptor:

```dart
class LoggerInterceptor extends InterceptorContract {
@override
Future<BaseRequest> interceptRequest({
BaseRequest interceptRequest({
required BaseRequest request,
}) async {
}) {
print('----- Request -----');
print(request.toString());
print(request.headers.toString());
return request;
}
@override
Future<BaseResponse> interceptResponse({
BaseResponse interceptResponse({
required BaseResponse response,
}) async {
}) {
log('----- Response -----');
log('Code: ${response.statusCode}');
if (response is Response) {
Expand All @@ -102,7 +104,7 @@ class LoggerInterceptor extends InterceptorContract {
```dart
class WeatherApiInterceptor implements InterceptorContract {
@override
Future<BaseRequest> interceptRequest({required BaseRequest request}) async {
FutureOr<BaseRequest> interceptRequest({required BaseRequest request}) async {
try {
request.url.queryParameters['appid'] = OPEN_WEATHER_API_KEY;
request.url.queryParameters['units'] = 'metric';
Expand All @@ -114,7 +116,10 @@ class WeatherApiInterceptor implements InterceptorContract {
}
@override
Future<BaseResponse> interceptResponse({required BaseResponse response}) async => response;
BaseResponse interceptResponse({
required BaseResponse response,
}) =>
response;
}
```

Expand All @@ -123,15 +128,15 @@ class WeatherApiInterceptor implements InterceptorContract {
```dart
class MultipartRequestInterceptor implements InterceptorContract {
@override
Future<BaseRequest> interceptRequest({required BaseRequest request}) async {
FutureOr<BaseRequest> interceptRequest({required BaseRequest request}) async {
if(request is MultipartRequest){
request.fields['app_version'] = await PackageInfo.fromPlatform().version;
}
return request;
}
@override
Future<BaseResponse> interceptResponse({required BaseResponse response}) async {
FutureOr<BaseResponse> interceptResponse({required BaseResponse response}) async {
if(response is StreamedResponse){
response.stream.asBroadcastStream().listen((data){
print(data);
Expand Down
8 changes: 4 additions & 4 deletions example/lib/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import 'package:http_interceptor/http_interceptor.dart';

class LoggerInterceptor extends InterceptorContract {
@override
Future<BaseRequest> interceptRequest({
BaseRequest interceptRequest({
required BaseRequest request,
}) async {
}) {
log('----- Request -----');
log(request.toString());
log(request.headers.toString());
Expand All @@ -15,9 +15,9 @@ class LoggerInterceptor extends InterceptorContract {
}

@override
Future<BaseResponse> interceptResponse({
BaseResponse interceptResponse({
required BaseResponse response,
}) async {
}) {
log('----- Response -----');
log('Code: ${response.statusCode}');
log('Response type: ${response.runtimeType}');
Expand Down
5 changes: 3 additions & 2 deletions example/lib/multipart_app.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';
Expand Down Expand Up @@ -171,8 +172,8 @@ class RemoveBgApiInterceptor extends InterceptorContract {
}

@override
Future<BaseResponse> interceptResponse({
BaseResponse interceptResponse({
required BaseResponse response,
}) async =>
}) =>
response;
}
14 changes: 8 additions & 6 deletions example/lib/weather_app.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
Expand Down Expand Up @@ -267,15 +268,15 @@ class WeatherRepository {
// var parsedWeather;
// try {
// var response = await InterceptedHttp.build(
// interceptors: [WeatherApiInterceptor()])
// .get("$baseUrl/weather", params: {'id': "$id"});
// interceptors: [WeatherApiInterceptor()],
// ).get('$baseUrl/weather', params: {'id': '$id'});
// if (response.statusCode == 200) {
// parsedWeather = json.decode(response.body);
// } else {
// throw Exception("Error while fetching. \n ${response.body}");
// throw Exception('Error while fetching. \n ${response.body}');
// }
// } catch (e) {
// log(e);
// log(e.toString());
// }
// return parsedWeather;
// }
Expand Down Expand Up @@ -326,8 +327,9 @@ class WeatherApiInterceptor extends InterceptorContract {
}

@override
Future<BaseResponse> interceptResponse(
{required BaseResponse response}) async =>
BaseResponse interceptResponse({
required BaseResponse response,
}) =>
response;
}

Expand Down
35 changes: 23 additions & 12 deletions lib/extensions/multipart_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,27 @@ extension MultipartRequestCopyWith on MultipartRequest {
bool? followRedirects,
int? maxRedirects,
bool? persistentConnection,
}) =>
MultipartRequest(
method?.asString ?? this.method,
url ?? this.url,
)
..headers.addAll(headers ?? this.headers)
..fields.addAll(fields ?? this.fields)
..files.addAll(files ?? this.files)
..followRedirects = followRedirects ?? this.followRedirects
..maxRedirects = maxRedirects ?? this.maxRedirects
..persistentConnection =
persistentConnection ?? this.persistentConnection;
}) {
var clonedRequest =
MultipartRequest(method?.asString ?? this.method, url ?? this.url)
..headers.addAll(headers ?? this.headers)
..fields.addAll(fields ?? this.fields);

for (var file in this.files) {
clonedRequest.files.add(MultipartFile(
file.field,
file.finalize(),
file.length,
filename: file.filename,
contentType: file.contentType,
));
}

this.persistentConnection =
persistentConnection ?? this.persistentConnection;
this.followRedirects = followRedirects ?? this.followRedirects;
this.maxRedirects = maxRedirects ?? this.maxRedirects;

return clonedRequest;
}
}
38 changes: 20 additions & 18 deletions lib/extensions/streamed_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,27 @@ extension StreamedRequestCopyWith on StreamedRequest {
int? maxRedirects,
bool? persistentConnection,
}) {
final req = StreamedRequest(
method?.asString ?? this.method,
url ?? this.url,
)
..headers.addAll(headers ?? this.headers)
..followRedirects = followRedirects ?? this.followRedirects
..maxRedirects = maxRedirects ?? this.maxRedirects
..persistentConnection =
persistentConnection ?? this.persistentConnection;
// Create a new StreamedRequest with the same method and URL
var clonedRequest =
StreamedRequest(method?.asString ?? this.method, url ?? this.url)
..headers.addAll(headers ?? this.headers);

if (stream != null) {
stream.listen((data) {
req.sink.add(data);
});
finalize().listen((data) {
req.sink.add(data);
});
}
// Use a broadcast stream to allow multiple listeners
var broadcastStream =
stream?.asBroadcastStream() ?? finalize().asBroadcastStream();

return req;
// Pipe the broadcast stream into the cloned request's sink
broadcastStream.listen((data) {
clonedRequest.sink.add(data);
}, onDone: () {
clonedRequest.sink.close();
});

this.persistentConnection =
persistentConnection ?? this.persistentConnection;
this.followRedirects = followRedirects ?? this.followRedirects;
this.maxRedirects = maxRedirects ?? this.maxRedirects;

return clonedRequest;
}
}
8 changes: 7 additions & 1 deletion lib/http/intercepted_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,13 @@ class InterceptedClient extends BaseClient {

final interceptedResponse = await _interceptResponse(response);

return interceptedResponse as StreamedResponse;
if (interceptedResponse is StreamedResponse) {
return interceptedResponse;
}

throw ClientException(
'Expected `StreamedResponse`, got ${interceptedResponse.runtimeType}.',
);
}

Future<BaseResponse> _sendUnstreamed({
Expand Down
4 changes: 2 additions & 2 deletions lib/models/interceptor_contract.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import 'package:http/http.dart';
///```dart
/// class LoggingInterceptor implements InterceptorContract {
/// @override
/// Future<BaseRequest> interceptRequest({required BaseRequest request}) async {
/// FutureOr<BaseRequest> interceptRequest({required BaseRequest request}) async {
/// print(request.toString());
/// return data;
/// }
///
/// @override
/// Future<BaseResponse> interceptResponse({required BaseResponse response}) async {
/// FutureOr<BaseResponse> interceptResponse({required BaseResponse response}) async {
/// print(response.toString());
/// return data;
/// }
Expand Down

0 comments on commit 1bcdcc0

Please sign in to comment.