Skip to content

Commit 47fc07a

Browse files
committed
docs: improve readme feature docs and list
1 parent 88efb75 commit 47fc07a

File tree

1 file changed

+108
-29
lines changed

1 file changed

+108
-29
lines changed

README.md

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ This is a plugin that lets you intercept the different requests and responses fr
3030
- [Using interceptors with Client](#using-interceptors-with-client)
3131
- [Using interceptors without Client](#using-interceptors-without-client)
3232
- [Retrying requests](#retrying-requests)
33+
- [Timeout configuration](#timeout-configuration)
3334
- [Using self signed certificates](#using-self-signed-certificates)
3435
- [InterceptedClient](#interceptedclient)
3536
- [InterceptedHttp](#interceptedhttp)
36-
- [Roadmap](#roadmap)
3737
- [Troubleshooting](#troubleshooting)
3838
- [Contributions](#contributions)
3939
- [Contributors](#contributors)
@@ -50,14 +50,15 @@ http_interceptor: <latest>
5050

5151
- 🚦 Intercept & change unstreamed requests and responses.
5252
- ✨ Retrying requests when an error occurs or when the response does not match the desired (useful for handling custom error responses).
53-
- 👓 `GET` requests with separated parameters.
53+
- 👓 `GET` requests with separated parameters using the `params` argument.
5454
- ⚡️ Standard `bodyBytes` on `ResponseData` to encode or decode in the desired format.
5555
- 🙌🏼 Array parameters on requests.
5656
- 🖋 Supports self-signed certificates (except on Flutter Web).
5757
- 🍦 Compatible with vanilla Dart projects or Flutter projects.
5858
- 🎉 Null-safety.
5959
- ⏲ Timeout configuration with duration and timeout functions.
6060
- ⏳ Configure the delay for each retry attempt.
61+
- 📁 Support for `MultipartRequest` and `StreamedRequest`/`StreamedResponse`.
6162

6263
## Usage
6364

@@ -69,10 +70,10 @@ import 'package:http_interceptor/http_interceptor.dart';
6970

7071
In order to implement `http_interceptor` you need to implement the `InterceptorContract` and create your own interceptor. This abstract class has four methods:
7172

72-
- `interceptRequest`, which triggers before the http request is called
73-
- `interceptResponse`, which triggers after the request is called, it has a response attached to it which the corresponding to said request;
74-
75-
- `shouldInterceptRequest` and `shouldInterceptResponse`, which are used to determine if the request or response should be intercepted or not. These two methods are optional as they return `true` by default, but they can be useful if you want to conditionally intercept requests or responses based on certain criteria.
73+
- `interceptRequest`, which triggers before the http request is called
74+
- `interceptResponse`, which triggers after the request is called, it has a response attached to it which the corresponding to said request;
75+
76+
- `shouldInterceptRequest` and `shouldInterceptResponse`, which are used to determine if the request or response should be intercepted or not. These two methods are optional as they return `true` by default, but they can be useful if you want to conditionally intercept requests or responses based on certain criteria.
7677

7778
You could use this package 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.
7879

@@ -89,33 +90,41 @@ class LoggerInterceptor extends InterceptorContract {
8990
print('----- Request -----');
9091
print(request.toString());
9192
print(request.headers.toString());
93+
print('Request type: ${request.runtimeType}');
9294
return request;
9395
}
9496
9597
@override
9698
BaseResponse interceptResponse({
9799
required BaseResponse response,
98100
}) {
99-
log('----- Response -----');
100-
log('Code: ${response.statusCode}');
101+
print('----- Response -----');
102+
print('Code: ${response.statusCode}');
103+
print('Response type: ${response.runtimeType}');
101104
if (response is Response) {
102-
log((response).body);
105+
print((response).body);
103106
}
104107
return response;
105108
}
106109
}
107110
```
108111

109-
- Changing headers with interceptor:
112+
- Changing headers and query parameters with interceptor:
110113

111114
```dart
112115
class WeatherApiInterceptor implements InterceptorContract {
113116
@override
114117
FutureOr<BaseRequest> interceptRequest({required BaseRequest request}) async {
115118
try {
119+
// Add query parameters to the URL
116120
request.url.queryParameters['appid'] = OPEN_WEATHER_API_KEY;
117121
request.url.queryParameters['units'] = 'metric';
122+
123+
// Set content type header
118124
request.headers[HttpHeaders.contentTypeHeader] = "application/json";
125+
126+
// Add custom headers
127+
request.headers['X-Custom-Header'] = 'custom-value';
119128
} catch (e) {
120129
print(e);
121130
}
@@ -130,14 +139,14 @@ class WeatherApiInterceptor implements InterceptorContract {
130139
131140
@override
132141
FutureOr<bool> shouldInterceptRequest({required BaseRequest request}) async {
133-
// You can conditionally intercept requests here
134-
return true; // Intercept all requests
142+
// Only intercept requests to weather API
143+
return request.url.host.contains('openweathermap.org');
135144
}
136145
137146
@override
138147
FutureOr<bool> shouldInterceptResponse({required BaseResponse response}) async {
139-
// You can conditionally intercept responses here
140-
return true; // Intercept all responses
148+
// Only intercept successful responses
149+
return response.statusCode >= 200 && response.statusCode < 300;
141150
}
142151
}
143152
```
@@ -149,31 +158,34 @@ class MultipartRequestInterceptor implements InterceptorContract {
149158
@override
150159
FutureOr<BaseRequest> interceptRequest({required BaseRequest request}) async {
151160
if(request is MultipartRequest){
161+
// Add metadata to multipart requests
152162
request.fields['app_version'] = await PackageInfo.fromPlatform().version;
163+
request.fields['timestamp'] = DateTime.now().toIso8601String();
153164
}
154165
return request;
155166
}
156167
157168
@override
158169
FutureOr<BaseResponse> interceptResponse({required BaseResponse response}) async {
159170
if(response is StreamedResponse){
171+
// Log streaming data
160172
response.stream.asBroadcastStream().listen((data){
161-
print(data);
173+
print('Streamed data: ${data.length} bytes');
162174
});
163175
}
164176
return response;
165177
}
166178
167179
@override
168180
FutureOr<bool> shouldInterceptRequest({required BaseRequest request}) async {
169-
// You can conditionally intercept requests here
170-
return true; // Intercept all requests
181+
// Only intercept multipart requests
182+
return request is MultipartRequest;
171183
}
172184
173185
@override
174186
FutureOr<bool> shouldInterceptResponse({required BaseResponse response}) async {
175-
// You can conditionally intercept responses here
176-
return true; // Intercept all responses
187+
// Only intercept streamed responses
188+
return response is StreamedResponse;
177189
}
178190
}
179191
```
@@ -197,8 +209,11 @@ class WeatherRepository {
197209
Future<Map<String, dynamic>> fetchCityWeather(int id) async {
198210
var parsedWeather;
199211
try {
200-
final response =
201-
await client.get("$baseUrl/weather".toUri(), params: {'id': "$id"});
212+
// Using the params argument for clean query parameter handling
213+
final response = await client.get(
214+
"$baseUrl/weather".toUri(),
215+
params: {'id': "$id"}
216+
);
202217
if (response.statusCode == 200) {
203218
parsedWeather = json.decode(response.body);
204219
} else {
@@ -228,8 +243,11 @@ class WeatherRepository {
228243
final http = InterceptedHttp.build(interceptors: [
229244
WeatherApiInterceptor(),
230245
]);
231-
final response =
232-
await http.get("$baseUrl/weather".toUri(), params: {'id': "$id"});
246+
// Using the params argument for clean query parameter handling
247+
final response = await http.get(
248+
"$baseUrl/weather".toUri(),
249+
params: {'id': "$id"}
250+
);
233251
if (response.statusCode == 200) {
234252
parsedWeather = json.decode(response.body);
235253
} else {
@@ -341,6 +359,73 @@ class ExpiredTokenRetryPolicy extends RetryPolicy {
341359
}
342360
```
343361

362+
### Timeout configuration
363+
364+
You can configure request timeouts and custom timeout handlers for both `InterceptedClient` and `InterceptedHttp`:
365+
366+
```dart
367+
// Configure timeout with custom handler
368+
final client = InterceptedClient.build(
369+
interceptors: [WeatherApiInterceptor()],
370+
requestTimeout: const Duration(seconds: 30),
371+
onRequestTimeout: () async {
372+
// Custom timeout handling
373+
print('Request timed out, returning default response');
374+
return StreamedResponse(
375+
Stream.value([]),
376+
408, // Request Timeout
377+
);
378+
},
379+
);
380+
381+
// Simple timeout without custom handler
382+
final http = InterceptedHttp.build(
383+
interceptors: [LoggerInterceptor()],
384+
requestTimeout: const Duration(seconds: 10),
385+
);
386+
```
387+
388+
### Working with bodyBytes
389+
390+
The library provides access to `bodyBytes` for both requests and responses, allowing you to work with binary data:
391+
392+
```dart
393+
class BinaryDataInterceptor implements InterceptorContract {
394+
@override
395+
FutureOr<BaseRequest> interceptRequest({required BaseRequest request}) async {
396+
if (request is Request) {
397+
// Access binary request data
398+
final bytes = request.bodyBytes;
399+
print('Request body size: ${bytes.length} bytes');
400+
401+
// Modify binary data if needed
402+
if (bytes.isNotEmpty) {
403+
// Example: compress data
404+
final compressed = await compressData(bytes);
405+
return request.copyWith(bodyBytes: compressed);
406+
}
407+
}
408+
return request;
409+
}
410+
411+
@override
412+
FutureOr<BaseResponse> interceptResponse({required BaseResponse response}) async {
413+
if (response is Response) {
414+
// Access binary response data
415+
final bytes = response.bodyBytes;
416+
print('Response body size: ${bytes.length} bytes');
417+
418+
// Example: decode binary data
419+
if (bytes.isNotEmpty) {
420+
final decoded = utf8.decode(bytes);
421+
print('Decoded response: $decoded');
422+
}
423+
}
424+
return response;
425+
}
426+
}
427+
```
428+
344429
### Using self signed certificates
345430

346431
You can achieve support for self-signed certificates by providing `InterceptedHttp` or `InterceptedClient` with the `client` parameter when using the `build` method on either of those, it should look something like this:
@@ -377,12 +462,6 @@ final http = InterceptedHttp.build(
377462

378463
_**Note:** It is important to know that since both HttpClient and IOClient are part of `dart:io` package, this will not be a feature that you can perform on Flutter Web (due to `BrowserClient` and browser limitations)._
379464

380-
## Roadmap
381-
382-
Check out our roadmap [here](https://doc.clickup.com/p/h/82gtq-119/f552a826792c049).
383-
384-
_We migrated our roadmap to better suit the needs for development since we use ClickUp as our task management tool._
385-
386465
## Troubleshooting
387466

388467
Open an issue and tell me, I will be happy to help you out as soon as I can.

0 commit comments

Comments
 (0)