grpc / grpc-dart

The Dart language implementation of gRPC.
https://pub.dev/packages/grpc
Apache License 2.0
850 stars 267 forks source link

Async client interceptors? #544

Open acoutts opened 2 years ago

acoutts commented 2 years ago

I would like to perform an async operation within an interceptor. The problem is it has to return a ResponseFuture, not a Future, so I cannot mark the interceptUnary method as async and perform asynchronous code within it (like access the platform method channel).

Is there a way to work around this to perform async operations within the interceptor?

3.0.2

Repro steps

  @override
  ResponseFuture<R> interceptUnary<Q, R>(
    ClientMethod<Q, R> method,
    Q request,
    CallOptions options,
    ClientUnaryInvoker<Q, R> invoker,
  ) async { } // error
esenmx commented 2 years ago

There are many issues and pull requests for this. Waiting https://github.com/grpc/grpc-dart/pull/489 to be merged.

lazicah commented 1 year ago

Any Solution?

EnesKaraosman commented 1 year ago

Any update/workaround?

mraleph commented 1 year ago

@EnesKaraosman you should probably use this code as a solution

EnesKaraosman commented 1 year ago

@EnesKaraosman you should probably use this code as a solution

Tried this one but received syntax issues with dart 2.18.4

mraleph commented 1 year ago

Tried this one but received syntax issues with dart 2.18.4

Well, you most likely can fix them. The code should still work but needs to be updated.

lazicah commented 1 year ago

Hi thanks for this solutions, but how I can return an custom error in the interceptor?

zs-dima commented 1 year ago

@mraleph it is very useful to have Async interceptStreaming as well. For example we want retrive Firebase Token in the interceptor - it will affect interceptStreaming and interceptUnary

Ideally:

  @override
  Stream<R> interceptStreaming<Q, R>(
    ClientMethod<Q, R> method,
    Stream<Q> requests,
    CallOptions options,
    ClientStreamingInvoker<Q, R> invoker,
  ) async* {
    final token = await _getToken?.call();
    yield* invoker(method, requests, options.withToken(token));
  }

  @override
  FutureOr<R> interceptUnary<Q, R>(
    ClientMethod<Q, R> method,
    Q request,
    CallOptions options,
    ClientUnaryInvoker<Q, R> invoker,
  ) async {
    final token = await _getToken?.call();
    return invoker(method, request, options.withToken(token));
  }
natebot13 commented 4 months ago

Came here looking for exactly the same need as @zs-dima . I would love to be able to just await in the intercept* methods. I've been trying to rework the workaround "retry" solution from @mraleph but I'm getting confused with the multiple layers of completers and futures.

natebot13 commented 4 months ago

After a lot of experimentation and nearly modifying the package for my own needs, I discovered metadataProviders which does exactly what I needed to modify the call options metadata before the request is made. I remember reading that field a while ago, but somehow ignored it, thinking an interceptor is what I really needed.

It turned out to be very simple:

@override
ResponseFuture<R> interceptUnary<Q, R>(
  ClientMethod<Q, R> method,
  Q request,
  CallOptions options,
  ClientUnaryInvoker<Q, R> invoker,
) {
  addToken(Map<String, String> metadata, String _) async {
    final token = await FirebaseAuth.instance.currentUser?.getIdToken();
    metadata['auth'] = token ?? '';
  }

  return invoker(
    method,
    request,
    options.mergedWith(CallOptions(providers: [addToken])),
  );
}