Nella pubblicazione precedente abbiamo visto come sia possibile ottenere le funzionalità dell'Health Check all'interno di un servizio gRPC. Oggi vedremo un altro middleware, molto comune nelle Web.API, utilizzato in un'applicazione gRPC, in grado di gestire gli errori in modo tale che la risposta alla chiamata rispetti un formato standardizzato e interpretabile.
Come per tutti i servizi l'implementazione deve partire dal file Program.cs, aggiungendo un servizio Interceptor all'interno delle opzioni di inizializzazione gRPC:
// Add services to the container. builder.Services.AddGrpc(options => { options.Interceptors.Add<LoggerInterceptor>(); });
Prima di analizzare il codice presente in LoggerInterceptor, modifichiamo il servizio GreeterService in modo tale che restituisca un errore, e creiamo un'applicazione console incaricata di effettuare le chiamate.
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) { throw new NotImplementedException("Errore"); }
Per configurare l'applicazione client dobbiamo ricordarci di importare i file .proto, come specificato nel nostro articolo su gRPC (https://www.aspitalia.com/articoli/asp.net-core5/grpc/usare-grpc-infrastruttura-nostri-servizi-web-p-3.aspx#title_1), successivamente possiamo procedere alla creazione di una nuova istanza GrpcChannel e chiamare l'endpoint modificato in precedenza.
var grpcEndpoint = "https://localhost:7030"; using var channel = GrpcChannel.ForAddress(grpcEndpoint); var c = new Greeter.GreeterClient(channel); try { var o = await c.SayHelloAsync(new HelloRequest { }); } catch (RpcException ex) { Console.WriteLine(ex.Message); }
Come si può notare, la classe dell'errore che riceveremo sarà di tipo RpcException. L'interceptor dovrà dunque intercettare qualsiasi tipo di eccezione che il servizio potrebbe emettere e sostituirla con un istanza di RpcException.
public class LoggerInterceptor : Interceptor { public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>( TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation) { try { return await continuation(request, context); } catch (Exception e) { throw new RpcException(new Status(StatusCode.Cancelled, e.Message)); } } }
Dettaglio degno di nota è la posizione di e.Message: benchè RpcException abbia un costruttore che accetti, come secondo argomento, il messaggio di errore, il client potrà leggere il messaggio solo se inserito all'interno di Status, il quale si occuperà dunque, sia dello stato dell'eccezione che della causa che l'ha generata.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Utilizzare una qualunque lista per i parametri di tipo params in C#
Supportare lo HierarchyID di Sql Server in Entity Framework 8
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API
Utilizzare un numero per gestire la concorrenza ottimistica con SQL Server ed Entity Framework
Eseguire script pre e post esecuzione di un workflow di GitHub
Estrarre dati randomici da una lista di oggetti in C#
Creare una libreria CSS universale: Immagini
Ricevere notifiche sui test con Azure Load Testing
Sfruttare gli embedding e la ricerca vettoriale con Azure SQL Database
Path addizionali per gli asset in ASP.NET Core MVC
Ordinare randomicamente una lista in C#
Managed deployment strategy in Azure DevOps