Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

gRPC İle Client Factory Integration

Merhaba,

gRPC kütüphanesi, client yapılanmasını merkezi hale getirip tasarımı kolaylaştırabilmek ve geleneksel gRPC client tasarımlarına alternatif olarak daha efektif geliştirmeler yapabilmek için gRPC Factory Integration özelliği sunmaktadır.

gRPC İle Client Factory Integration

Geleneksel client yapılanması…


Yukarıdaki geleneksel yapılanma, gRPC client tasarımlarında yönetilebilirlik açısından pek tercih edilebilir bir düzenleme değildir. Haliyle bizlere merkezi bir konum üzerinden client tasarımını sağlayacak olan Factory entegrasyonunu tercih edebiliriz.

Factory Integration’ı kullanabilmek için temel gRPC kütüphanelerinin yanında Grpc.Net.ClientFactory kütüphanesinin uygulamaya yüklenmesi yeterlidir. İlgili entegrasyonu yapabilmek için ‘Startup.cs’ dosyasında aşağıdaki gibi AddGrpcClient servisinin çağrılması ve temel konfigürasyonların yapılması gerekmektedir.

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpcClient<PersonClient>(o =>
            {
                o.Address = new Uri("https://localhost:5001");
            });
            .
            .
            .
        }
        .
        .
        .
    }

Bu işlemden sonra gRPC client sınıfı ilgili uygulamanın IoC container’ına eklenecektir. Haliyle client ilgili sınıfa doğrudan dependency injection ile erişebilecek ve tüketebilecektir.
Misal controller’dan direkt talep edilebilmektedir :

    [Route("api/[controller]")]
    [ApiController]
    public class PersonController : ControllerBase
    {
        readonly PersonClient _personClient;
        public PersonController(PersonClient personClient)
        {
            _personClient = personClient;
        }
        public async Task<IActionResult> Get()
        {
            var response = _personClient.GetPersons(new());
            while (await response.ResponseStream.MoveNext(new()))
            {
                Console.WriteLine(response.ResponseStream.Current.Name);
            }
            return Ok();
        }
    }

Ya da custom bir servis tarafından da çağrılabilmektedir :

    public class PersonService 
    {
        readonly PersonClient _personClient;
        public PersonService(PersonClient personClient)
        {
            _personClient = personClient;
        }
        public async Task GetPersons()
        {
            var response = _personClient.GetPersons(new());
            while (await response.ResponseStream.MoveNext(new()))
            {
                Console.WriteLine(response.ResponseStream.Current.Name);
            }
        }
    }
Interceptor Konfigürasyonu

Client’tan gelen çağrılara karşılık interceptor’ları aşağıdaki gibi devreye sokabilirsiniz.
Interceptor :

    public class CustomInterceptor : Interceptor
    {
        public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
        {
            Console.WriteLine("Merhaba dünya...");
            return base.AsyncServerStreamingCall(request, context, continuation);
        }
    }

Konfigürasyon :

            services.AddGrpcClient<PersonClient>(o =>
            {
                o.Address = new Uri("https://localhost:5001");
            })
                .AddInterceptor(() => new CustomInterceptor())
                .AddInterceptor(() => new CustomInterceptor2());
Channel Konfigürasyonu

Client’tan gelen çağrılara istinaden basic düzeyde bir kimlik doğrulama işlemi gerçekleştirebilmek için channel konfigürasyonunu aşağıdaki gibi devreye sokabilirsiniz.

Örneğin, gRPC Server’da base olarak kullanılan ‘PersonService’in ‘Authorize’ edildiğini varsayalım.

    [Authorize]
    public class PersonService : PersonBase
    {
        public async override Task<PersonResponse> GetPerson(Empty request, ServerCallContext context)
        {
            .
            .
            .
        }
        public async override Task GetPersons(Empty request, IServerStreamWriter<PersonResponse> responseStream, ServerCallContext context)
        {
            .
            .
            .
        }
    }

Bu servise erişebilmek için JWT ile istek yapılacaksa eğer bu isteği client’ta aşağıdaki gibi ‘ConfigureChannel’ metodu ile yapılandırmak gerekmektedir.

            services.AddGrpcClient<PersonClient>(o =>
            {
                o.Address = new Uri("https://localhost:5001");
            })
                .AddInterceptor(() => new CustomInterceptor())
                .AddInterceptor(() => new CustomInterceptor())
                .ConfigureChannel(option =>
                {
                    option.Credentials = ChannelCredentials.Create(new SslCredentials(), CallCredentials.FromInterceptor((context, metadata) =>
                    {
                        string jwt = "...";
                        metadata.Add("Authorization", $"Bearer {jwt}");
                        return Task.CompletedTask;
                    }));
                });
Named Clients/İsimlendirilmiş Client’lar

Tipik olarak bir gRPC client’ı bir kez IoC’ye kaydedilir ve ardından her ihtiyaç olduğunda dependency injection ile bu nesne talep edilir. Ancak bazen bir istemcinin aynı client nesnesine birden fazla konfigüre edilmiş şekilde sahip olması gerekeceği senaryolar olabilir. Misal olarak bir istemci x client’ına hem normal olarak sahiptir hem de ilgili client’ın Credentials bilgileri girilmiş versiyonuna da ihtiyacı olabilir. İşte böyle durumlara istinaden Named Clients özelliği oldukça efektif bir çözüm sunmaktadır.

Misal :

            //Kimlik Doğrulamasız Client
            services.AddGrpcClient<PersonClient>("PersonClient", o =>
            {
                o.Address = new Uri("https://localhost:5001");
            });
            //Kimlik Doğrulamalı Client
            services.AddGrpcClient<PersonClient>("PersonClientAuthenticated", o =>
            {
                o.Address = new Uri("https://localhost:5001");
            })
            .ConfigureChannel(option =>
            {
                option.Credentials = ChannelCredentials.Create(new SslCredentials(), CallCredentials.FromInterceptor((context, metadata) =>
                {
                    string jwt = "...";
                    metadata.Add("Authorization", $"Bearer {jwt}");
                    return Task.CompletedTask;
                }));
            });

Yukarıdaki kod bloğuna göz atarsanız eğer ‘PersonClient’ın hem kimlik doğrulamalı hem de kimlik doğrulamasız olmak üzere iki farklı konfigüre edilmiş hali IoC’ye verilmiştir ve bu nesneler arasındaki fark ilk parametrelerdeki isimlendirmelerle sağlanmıştır. Bu adımdan sonra ihtiyaca binaen bir nesneyi dependency injection ile elde etmek istiyorsanız aşağıdaki gibi ‘GrpcClientFactory’ nesnesi üzerinden talep etmeniz yeterli olacaktır.

    public class PersonService
    {
        readonly PersonClient _personClient;
        readonly PersonClient _personClientAuthenticated;
        public PersonService(GrpcClientFactory grpcClientFactory)
        {
            _personClientAuthenticated = grpcClientFactory.CreateClient<PersonClient>("PersonClientAuthenticated");
            _personClient = grpcClientFactory.CreateClient<PersonClient>("PersonClient");
        }
        .
        .
        .
    }

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

1 Cevap

  1. 24 Ekim 2021

    […] gRPC Client Factory ile kimlik doğrulamayı gerçekleştirmek istiyorsanız eğer bu işlem aşağıdaki gibi basitçe halledilebilmektedir. […]

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir