Skip to content

Commit 0b417a6

Browse files
committed
Merge branch 'develop' of https://github.com/CodingAleCR/http_interceptor into release/0.3.0
2 parents abe70c5 + 2a70152 commit 0b417a6

File tree

5 files changed

+93
-23
lines changed

5 files changed

+93
-23
lines changed

README.md

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,32 @@
66
[![codecov](https://codecov.io/gh/CodingAleCR/http_interceptor/branch/master/graph/badge.svg)](https://codecov.io/gh/CodingAleCR/http_interceptor)
77
[![Star on GitHub](https://img.shields.io/github/stars/codingalecr/http_interceptor.svg?style=flat&logo=github&colorB=deeppink&label=stars)](https://github.com/codingalecr/http_interceptor)
88

9-
A middleware library that lets you modify requests and responses if desired. Based of on [http_middleware](https://github.com/TEDConsulting/http_middleware)
9+
This is a plugin that lets you intercept the different requests and responses from Dart's http package. You can use to add headers, modify query params, or print a log of the response.
1010

11-
## Getting Started
11+
## Quick Reference
1212

13-
This is a plugin that lets you intercept the different requests and responses from Dart's http package. You can use to add headers, modify query params, or print a log of the response.
13+
- [Installation](#installation)
14+
- [Usage](#usage)
15+
- [Building your own interceptor](#building-your-own-interceptor)
16+
- [Using your interceptor](#using-your-interceptor)
17+
- [Retrying requests](#retrying-requests)
18+
- [Having trouble? Fill an issue](#troubleshooting)
1419

15-
### Installing
20+
## Installation
1621

1722
Include the package with the latest version available in your `pubspec.yaml`.
1823

1924
```dart
2025
http_interceptor: any
2126
```
2227

23-
### Importing
28+
## Usage
2429

2530
```dart
2631
import 'package:http_interceptor/http_interceptor.dart';
2732
```
2833

29-
### Using `http_interceptor`
30-
31-
#### Building your own interceptor
34+
### Building your own interceptor
3235

3336
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.
3437

@@ -72,11 +75,11 @@ class WeatherApiInterceptor implements InterceptorContract {
7275
}
7376
```
7477

75-
#### Using your interceptor
78+
### Using your interceptor
7679

7780
Now that you actually have your interceptor implemented, now you need to use it. There are two general ways in which you can use them: by using the `HttpWithInterceptor` to do separate connections for different requests or using a `HttpClientWithInterceptor` for keeping a connection alive while making the different `http` calls. The ideal place to use them is in the service/provider class or the repository class (if you are not using services or providers); if you don't know about the repository pattern you can just google it and you'll know what I'm talking about. ;)
7881

79-
##### Using interceptors with Client
82+
#### Using interceptors with Client
8083

8184
Normally, this approach is taken because of its ability to be tested and mocked.
8285

@@ -107,7 +110,7 @@ class WeatherRepository {
107110
}
108111
```
109112

110-
##### Using interceptors without Client
113+
#### Using interceptors without Client
111114

112115
This is mostly the straight forward approach for a one-and-only call that you might need intercepted.
113116

@@ -137,6 +140,27 @@ class WeatherRepository {
137140
}
138141
```
139142

140-
### Issue Reporting
143+
### Retrying requests
144+
145+
**(NEW 🎉)** Sometimes you need to retry a request due to different circumstances, an expired token is a really good example. Here's how you could potentially implement an expired token retry policy with `http_interceptor`.
146+
147+
```dart
148+
class ExpiredTokenRetryPolicy extends RetryPolicy {
149+
@override
150+
bool shouldAttemptRetryOnResponse(Response response) {
151+
if (response.statusCode == 401) {
152+
// Perform your token refresh here.
153+
154+
return true;
155+
}
156+
157+
return false;
158+
}
159+
}
160+
```
161+
162+
You can also set the maximum amount of retry attempts with `maxRetryAttempts` property or override the `shouldAttemptRetryOnException` if you want to retry the request after it failed with an exception.
163+
164+
## Troubleshooting
141165

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

lib/http_client_with_interceptor.dart

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,21 @@ import 'http_methods.dart';
3737
class HttpClientWithInterceptor extends http.BaseClient {
3838
List<InterceptorContract> interceptors;
3939
Duration requestTimeout;
40+
RetryPolicy retryPolicy;
4041

4142
final Client _client = Client();
43+
int _retryCount = 0;
4244

43-
HttpClientWithInterceptor._internal({this.interceptors, this.requestTimeout});
45+
HttpClientWithInterceptor._internal({
46+
this.interceptors,
47+
this.requestTimeout,
48+
this.retryPolicy,
49+
});
4450

4551
factory HttpClientWithInterceptor.build({
4652
@required List<InterceptorContract> interceptors,
4753
Duration requestTimeout,
54+
RetryPolicy retryPolicy,
4855
}) {
4956
assert(interceptors != null);
5057

@@ -53,6 +60,7 @@ class HttpClientWithInterceptor extends http.BaseClient {
5360
return HttpClientWithInterceptor._internal(
5461
interceptors: interceptors,
5562
requestTimeout: requestTimeout,
63+
retryPolicy: retryPolicy,
5664
);
5765
}
5866

@@ -137,7 +145,8 @@ class HttpClientWithInterceptor extends http.BaseClient {
137145
} else if (url is Uri) {
138146
url = addParametersToUrl(url, params);
139147
} else {
140-
throw HttpInterceptorException("Malformed URL parameter");
148+
throw HttpInterceptorException(
149+
"Malformed URL parameter. Check that the url used is either a String or a Uri instance.");
141150
}
142151

143152
Request request = new Request(methodToString(method), url);
@@ -155,14 +164,7 @@ class HttpClientWithInterceptor extends http.BaseClient {
155164
}
156165
}
157166

158-
// Intercept request
159-
request = await _interceptRequest(request);
160-
161-
var stream = requestTimeout == null
162-
? await send(request)
163-
: await send(request).timeout(requestTimeout);
164-
165-
var response = await Response.fromStream(stream);
167+
var response = await _attemptRequest(request);
166168

167169
// Intercept response
168170
response = await _interceptResponse(response);
@@ -180,6 +182,37 @@ class HttpClientWithInterceptor extends http.BaseClient {
180182
throw new ClientException("$message.", url);
181183
}
182184

185+
Future<Response> _attemptRequest(Request request) async {
186+
var response;
187+
try {
188+
// Intercept request
189+
request = await _interceptRequest(request);
190+
191+
var stream = requestTimeout == null
192+
? await send(request)
193+
: await send(request).timeout(requestTimeout);
194+
195+
response = await Response.fromStream(stream);
196+
if (retryPolicy != null
197+
&& retryPolicy.maxRetryAttempts > _retryCount
198+
&& retryPolicy.shouldAttemptRetryOnResponse(response)) {
199+
_retryCount += 1;
200+
return _attemptRequest(request);
201+
}
202+
} catch (error) {
203+
if (retryPolicy != null
204+
&& retryPolicy.maxRetryAttempts > _retryCount
205+
&& retryPolicy.shouldAttemptRetryOnException(error)) {
206+
_retryCount += 1;
207+
return _attemptRequest(request);
208+
} else {
209+
throw HttpInterceptorException(error.toString());
210+
}
211+
}
212+
213+
return response;
214+
}
215+
183216
/// This internal function intercepts the request.
184217
Future<Request> _interceptRequest(Request request) async {
185218
for (InterceptorContract interceptor in interceptors) {

lib/http_with_interceptor.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,18 @@ import 'package:http_interceptor/interceptor_contract.dart';
2828
class HttpWithInterceptor {
2929
List<InterceptorContract> interceptors;
3030
Duration requestTimeout;
31+
RetryPolicy retryPolicy;
3132

3233
HttpWithInterceptor._internal({
3334
this.interceptors,
3435
this.requestTimeout,
36+
this.retryPolicy,
3537
});
3638

3739
factory HttpWithInterceptor.build({
3840
@required List<InterceptorContract> interceptors,
3941
Duration requestTimeout,
42+
RetryPolicy retryPolicy,
4043
}) {
4144
assert(interceptors != null);
4245

@@ -45,6 +48,7 @@ class HttpWithInterceptor {
4548
return new HttpWithInterceptor._internal(
4649
interceptors: interceptors,
4750
requestTimeout: requestTimeout,
51+
retryPolicy: retryPolicy,
4852
);
4953
}
5054

@@ -91,6 +95,7 @@ class HttpWithInterceptor {
9195
var client = new HttpClientWithInterceptor.build(
9296
interceptors: interceptors,
9397
requestTimeout: requestTimeout,
98+
retryPolicy: retryPolicy,
9499
);
95100
try {
96101
return await fn(client);

lib/models/models.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export 'request_data.dart';
22
export 'response_data.dart';
3-
export 'http_interceptor_exception.dart';
3+
export 'http_interceptor_exception.dart';
4+
export 'retry_policy.dart';

lib/models/retry_policy.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import 'package:http/http.dart';
2+
3+
abstract class RetryPolicy {
4+
bool shouldAttemptRetryOnException(Exception reason) => false;
5+
bool shouldAttemptRetryOnResponse(Response response) => false;
6+
final int maxRetryAttempts = 1;
7+
}

0 commit comments

Comments
 (0)