网络拦截器

网络请求拦截器

有时候我们需要对 Kraken 内部发出的请求进行拦截或者替换,这种能力通常被用来实现自定义缓存、错误率统计、鉴权等功能。

Kraken 在 Widget 层提供了 httpClientInterceptor 参数,可以通过实现自定义的 HttpClientInterceptor 来实现网络拦截器。

抽象类方法说明

我们需要实现 HttpClientInterceptor 来提供网络拦截的能力,它有 3 个方法:

  • Future<HttpClientRequest?> beforeRequest(HttpClientRequest request):发出请求之前,这个时候 HttpClientRequest 对象已经创建,它会作为这个方法的第一个参数被传入,你可以直接修改此对象,或者你也可以返回一个新的 HttpClientRequest 对象。返回 Null 值意味着不进行任何修改。
  • Future<HttpClientResponse?> shouldInterceptRequest(HttpClientRequest request):在发起请求时触发,与 beforeRequest 不同的是,你可以直接返回一个 HttpClientResponse 对象,这个时候 Kraken 将不再发起真实的网络请求,而是使用此对象作为此次请求的返回对象。这在实现自定义缓存功能的时候会比较有用。返回 Null 值意味着不进行任何修改。
  • Future<HttpClientResponse?> afterResponse(HttpClientRequest request, HttpClientResponse response);:在请求返回后触发,会同时传入 HttpClientRequestHttpClientResponse 对象;但是在这个时候 HttpClientRequest 是只读对象,修改它无任何意义;HttpClientResponse 可以进行修改,或者你也可以返回一个新的 HttpClientResponse 对象作为此次请求的返回对象。返回 Null 值意味着不进行任何修改。

Tips: HttpClientResponse 也是一个抽象类,你可以使用 HttpClientStreamResponse 作为 HttpClientResponse 的实际实现。

一个简单的例子

  1. import 'dart:typed_data';
  2. import 'package:kraken/foundation.dart';
  3. class CustomHttpClientInterceptor implements HttpClientInterceptor {
  4. DateTime? _startTime;
  5. DateTime? _endTime;
  6. @override
  7. Future<HttpClientRequest?> beforeRequest(HttpClientRequest request) async {
  8. request.headers.set('x-foo', 'bar');
  9. print('beforeRequest, request headers: ${request.headers}');
  10. if (request.uri.path == '/posts/1') {
  11. _startTime = DateTime.now();
  12. }
  13. return null;
  14. }
  15. @override
  16. Future<HttpClientResponse?> afterResponse(HttpClientRequest request, HttpClientResponse response) async {
  17. print('afterResponse, response headers: ${response.headers}');
  18. if (request.uri.path == '/posts/1') {
  19. _endTime = DateTime.now();
  20. print('/posts/1 cost: ${_endTime!.millisecondsSinceEpoch - _startTime!.millisecondsSinceEpoch}ms');
  21. }
  22. return null;
  23. }
  24. @override
  25. Future<HttpClientResponse?> shouldInterceptRequest(HttpClientRequest request) async {
  26. if (request.uri.path == '/posts/1') {
  27. // Direct output string, transform to stream.
  28. String replaced = '{ "foo": "bar" }';
  29. Uint8List data = Uint8List.fromList(replaced.codeUnits);
  30. Stream<Uint8List> stream = Stream<Uint8List>.value(data);
  31. return HttpClientStreamResponse('text/html', 'utf8', stream, responseHeaders: { 'x-kraken': 'hey', 'hello': 'world' });
  32. }
  33. return null;
  34. }
  35. }
  36. void main() {
  37. runApp(Kraken(
  38. bundleContent: '''
  39. fetch('https://jsonplaceholder.typicode.com/posts/1')
  40. .then(function(response) {
  41. return response.json();
  42. })
  43. .then(function(data) {
  44. // This is the object from API.
  45. console.log('LOG:', data);
  46. })
  47. .catch(err => {
  48. console.error('error:', err);
  49. });
  50. ''',
  51. httpClientInterceptor: CustomHttpClientInterceptor(),
  52. ));
  53. }

自定义 URI 解析

Kraken 会在内部实现一套 URI 的解析规则 UriParser,当开发者需要自定义一套 URI 解析规则时候,可以对 UriParser 进行处理。

一个简单的例子

  1. class MyUriParser extends UriParser {
  2. @override
  3. String resolve(Uri base, Uri relative) {
  4. String uri = super.resolve(base, relative);
  5. // custom parse uri.
  6. return uri;
  7. }
  8. }
  9. void main() {
  10. runApp(Kraken(
  11. uriParser: MyUriParser(),
  12. ));
  13. }