CodingAleCR / http_interceptor

A lightweight, simple plugin that allows you to intercept request and response objects and modify them if desired.
MIT License
134 stars 67 forks source link

interceptRequest never called #33

Closed smedic closed 4 years ago

smedic commented 4 years ago

I am trying to put auth token inside of a call, but my request interceptor is never called:

final http.Client client = HttpClientWithInterceptor.build(interceptors: [
    ApiInterceptor(),
  ]);

  Future<http.Response> request(RequestMethod method,
      {String path, Map<String, String> headers, Object body}) async {
    final uri = _constructUri(path);

    var request = http.Request(method.value, uri);

    if (headers != null) request.headers.addAll(_generateHeaders(headers));
    if (body != null) {
      if (body is String) {
        request.body = body;
      } else if (body is List || body is Map<String, dynamic>) {
        request.body = jsonEncode(body);
      } else {
        throw ArgumentError('Invalid request body "$body".');
      }
    }
    final stream = await client.send(request);
    return http.Response.fromStream(stream);
  }

This is how interceptor looks like:

class ApiInterceptor implements InterceptorContract {
  @override
  Future<RequestData> interceptRequest({RequestData data}) async {
    print("Intercept call");
    final token = await getToken();
    try {
      data.params[HttpHeaders.authorizationHeader] = 'Bearer ${token}';
    } catch (e) {
      print(e);
    }
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async => data;
}

Any idea why? I need to be attached to client so every call passes through it. Thanks

issue-label-bot[bot] commented 4 years ago

Issue-Label Bot is automatically applying the label question to this issue, with a confidence of 0.76. Please mark this comment with :thumbsup: or :thumbsdown: to give our bot feedback!

Links: app homepage, dashboard and code for this bot.

CodingAleCR commented 4 years ago

Hi, there are a couple of things wrong in terms of usage, let's see them:

  1. Currently the plugin does not support the send method, it is only available due to compatibility issues at the moment (but I hope to fix this in a version soon, along with multipart request support).
  2. I imagine that the reason you are doing this "wrapper" is to avoid creating multiple instances of the same client+interceptor combination. I would change a couple of things, I would avoid using the requests method as an enum or string if you are using this particular plugin, this because I think it's more sustainable to use the methods exposed by the plugin to make requests. Anyway, here's the way I would implement it, hope it helps:
class MyApiClientWrapper {
  final http.Client client = HttpClientWithInterceptor.build(interceptors: [
    ApiInterceptor(),
  ]);

  String _parseBody(Object body) {
    var newBody = null;
    if (body != null) {
      if (body is String) {
        newBody = body;
      } else if (body is List || body is Map<String, dynamic>) {
        newBody = jsonEncode(body);
      } else {
        throw ArgumentError('Invalid request body "$body".');
      }
    }
    return newBody;
  }

  Future<Response> head(url, {Map<String, String> headers}) =>
      client.head(_constructUri(path), headers: _generateHeaders(headers));

  Future<Response> get(url,
          {Map<String, String> headers, Map<String, String> params}) =>
      client.get(
        _constructUri(path),
        headers: _generateHeaders(headers),
        params: params,
      );

  Future<Response> post(url,
          {Map<String, String> headers, body, Encoding encoding}) =>
      client.post(
        _constructUri(path),
        headers: _generateHeaders(headers),
        body: _parseBody(body),
        encoding: encoding,
      );

  Future<Response> put(url,
          {Map<String, String> headers, body, Encoding encoding}) =>
      client.put(
        _constructUri(path),
        headers: _generateHeaders(headers),
        body: _parseBody(body),
        encoding: encoding,
      );

  Future<Response> patch(url,
          {Map<String, String> headers, body, Encoding encoding}) =>
      client.patch(
        _constructUri(path),
        headers: _generateHeaders(headers),
        body: _parseBody(body),
        encoding: encoding,
      );

  Future<Response> delete(url, {Map<String, String> headers}) =>
      client.delete(
        _constructUri(path),
        headers: _generateHeaders(headers),
      );
}

Interceptor:

class ApiInterceptor implements InterceptorContract {
  @override
  Future<RequestData> interceptRequest({RequestData data}) async {
    print("Intercept call");
    final token = await getToken();
    try {
      data.params[HttpHeaders.authorizationHeader] = 'Bearer ${token}';
    } catch (e) {
      print(e);
    }
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async => data;
}

Now, if what you needed was a Stream response, as of right now we don't support that.

smedic commented 4 years ago

Thanks for suggestions. I did not know that send is not supported. I really appreciate your work on this. +1 :)

CodingAleCR commented 4 years ago

No problem! I am really happy to help, I will probably mark the method somehow to avoid confusions like this because I can definitely understand how this feels like a bug (and since it's probably not well documented it could be referred as one). I hope it helped you out at least a bit. Cheers!