Comment ajouter gRPC à votre application Blazor

Cet article explique comment démarrer avec gRPC dans vos applications Blazor. Nous commencerons par une application Blazor WebAssembly de base hébergée à partir d'un serveur ASP.NET Core, puis la modifierons pour utiliser gRPC.
Le protocole de communication standard entre les clients frontaux, comme Blazor, et les serveurs ont été des API RESTful utilisant JSON données par défaut depuis plus de quelques années maintenant. Les implémentations les plus typiques suivent des modèles stricts
qui sont parfaits pour l'uniformité, mais cela rend les choses un peu difficiles à modéliser dans de nombreux scénarios. Parfois, ce serait génial si vous pouviez simplement utiliser un texte plus lisible par l'homme comme addItem
createOrUpdateEvent
ou addAttendeeToEvent
ce qui peut être un peu difficile avec le exigences de REST.
C'est là que gRPC entre dans l'image et peut résoudre certains de ces problèmes. gRPC est une réinvention de RPC (Remote Procedure Call) avec des avantages multilingues. gRPC permet à votre code frontal d'appeler des serveurs backend en utilisant des tampons de protocole d'une manière indépendante du langage et vous permet de prédéfinir des objets pour envoyer et recevoir des données. Un autre avantage supplémentaire de l'utilisation de gRPC est que les données peuvent être transmises au format binaire, ce qui peut entraîner une réduction considérable de la quantité de données transmises sur le fil, améliorant ainsi considérablement les performances.
Récemment, Microsoft a publié gRPC pour Web pour Blazor pour nous permettre de profiter pleinement de ce nouveau protocole pour envoyer des données dans les deux sens depuis le serveur. La grande chose à propos de cette version est que gRPC a un support de première classe dans Blazor au lieu d'être corrigé après coup.
Je vais vous expliquer à quel point il est facile de démarrer avec gRPC dans vos applications Blazor en en commençant par une application Blazor WebAssembly de base hébergée à partir d'un serveur ASP.NET Core, puis en la modifiant pour utiliser gRPC. Allons-y.
Création de notre projet Blazor de base
Premièrement, nous allons commencer par configurer une application et un serveur Blazor de base dans le même projet. Pour commencer, nous voulons simplement créer notre application Blazor et notre serveur à l'aide du modèle Blazor WebAssembly et du modèle de serveur standard.
Ensuite, nous allons créer le serveur pour héberger notre application Blazor WebAssembly.
Maintenant que nous avoir une application Blazor fonctionnelle, nous allons commencer à modifier les points de terminaison à servir sur gRPC et à modifier l'application Blazor.
Voici un exemple fonctionnel du code à ce stade.
Ajout de points de terminaison gRPC à Serveur
Nous allons commencer par modifier le serveur pour prendre en charge les points de terminaison gRPC avant de mettre à jour le client. Cela nous permettra d'ajouter la prise en charge de gRPC sans casser notre système. Pour commencer, nous allons modifier le serveur en ajoutant des packages NuGet pour gRPC:
- Ajouter des packages NuGet au serveur
Grpc.AspNetCore
Grpc.AspNetCore.Web
Une fois les packages ajoutés, nous avons besoin pour ajouter notre définition de protocole BeachConditions dans Protos / BeachConditions.proto
. Le fichier proto va définir les appels de service avec les entrées et les sorties. L'un des grands avantages de la définition de notre service à l'aide de protos est que le code sera généré pour nous au moment de la compilation, nous aurons juste à remplir l'implémentation pour chaque appel de service.
syntax = "proto3";
option csharp_namespace = "Protos";
package beachConditions;
// Les services de conditions de plage qui définissent les appels
service BeachConditions {
rpc getAllBeachConditions (GetAllBeachConditionRequest) renvoie (GetAllBeachConditionResponse);
rpc createBeachCondition (CreateBeachConditionRequest) renvoie (CreateBeachConditionResponse);
}
// objet de requête pour getAllBeachConditions
message GetAllBeachConditionRequest {
}
// objet de réponse pour getAllBeachConditions
message GetAllBeachConditionResponse {
répété BeachCondition beachConditions = 1;
}
// objet de requête pour createBeachCondition
message CreateBeachConditionRequest {
nom de chaîne = 1;
condition de chaîne = 2;
}
// objet de réponse pour createBeachCondition
message CreateBeachConditionResponse {
BeachCondition createdBeachCondition = 1;
}
// objet commun
message BeachCondition {
int64 beachId = 1;
nom de chaîne = 2;
condition de chaîne = 3;
}
Pour référence: BeachConditions.proto .
Après avoir ajouté notre définition commune, nous devons ajouter notre code de service C # pour ce tampon de protocole. Donc, nous allons ajouter Services / BeachConditionsService.cs
. Plus tôt, j'ai dit que le fichier .proto générerait le service pour nous, et il l'a fait en générant le BeachConditions.BeachConditionsBase
. C'est à nous d'implémenter le code.
en utilisant Grpc.Core;
en utilisant Microsoft.Extensions.Logging;
en utilisant Protos;
en utilisant le système;
using System.Threading.Tasks;
espace de noms Server.Services
{
public class BeachConditionsService: BeachConditions.BeachConditionsBase
{
privé en lecture seule ILogger _logger;
public BeachConditionsService (ILogger logger)
{
_logger = enregistreur;
}
Tâche de remplacement public getAllBeachConditions (requête GetAllBeachConditionRequest, contexte ServerCallContext)
{
GetAllBeachConditionResponse result = new GetAllBeachConditionResponse ();
result.BeachConditions.Add (new BeachCondition {BeachId = 1, Name = "North Point", Condition = "Sandy"});
result.BeachConditions.Add (new BeachCondition {BeachId = 2, Name = "South Point", Condition = "Murky"});
return Task.FromResult (résultat);
}
Tâche de remplacement public createBeachCondition (demande CreateBeachConditionRequest, contexte ServerCallContext)
{
BeachCondition beachCondition = nouveau BeachCondition
{
BeachId = new Random (). Next (),
Nom = demande.Nom,
Condition = demande.
};
Réponse CreateBeachConditionResponse = nouveau CreateBeachConditionResponse
{
CreatedBeachCondition = beachCondition
};
return Task.FromResult (réponse);
}
}
}
Pour référence: BeachConditionsService.cs .
Maintenant que nous avons défini notre service en le connectant à notre implémentation, nous devons l'ajouter au serveur pour lui permettre d'être exposé. Pour ce faire, nous devons modifier notre fichier Startup.cs
dans le projet Server.
- Ajouter
services.AddGrpc ();
auConfigureServices
] méthode - Ajoutez
app.UseGrpcWeb ();
à la méthodeConfigure
- Add
endpoints.MapGrpcService
à la configuration du point de terminaison de la méthode(). EnableGrpcWeb (); Configure
- Cette ligne injecte le BeachConditionsService dans le serveur en indiquant à notre serveur qu'il doit être hébergé sur gRPC Web
Pour référence: Startup.cs .
À ce stade, notre serveur peut exécuter et héberger des points de terminaison gRPC et non gRPC, tout en hébergeant notre application Blazor. Il s'agit d'une étape assez importante car vous remarquerez que nous hébergeons des appels d'API gRPC et REST à partir du même serveur, ainsi que l'application Blazor. Vous pouvez certainement l'utiliser à votre avantage lorsque vous migrez vers l'utilisation de gRPC là où cela a du sens.
Modification de notre application pour prendre en charge gRPC
Il est maintenant temps pour nous de mettre à jour notre client Blazor. Pour commencer, nous allons devoir ajouter des packages NuGet avec l'ajout de références dans _imports.razor
:
- Ajouter des packages NuGet au client
Grpc.Net.Client.Web
Grpc .Net.Client
Grpc.Tools
Google.Protobuf
- Ajoutez
@using Grpc.Net.Client
au_imports.razor
Maintenant que notre application Blazor a toutes les références nécessaires, nous devons commencer à injecter certains services pour nous permettre d'accéder aux canaux gRPC au niveau de la page. Tout d'abord, nous allons modifier Program.cs
en ajoutant le code pour définir le canal gRPC.
// Ajouter un service gRPC
builder.Services.AddSingleton (services =>
{
// Récupère l'adresse du service depuis appsettings.json
var config = services.GetRequiredService ();
var backendUrl = "https: // localhost: 5001";
// Créer un canal avec un GrpcWebHandler adressé au serveur backend.
//
// GrpcWebText est utilisé car le streaming serveur le requiert. Si le streaming serveur n'est pas utilisé dans votre application
// alors GrpcWeb est recommandé car il produit des messages plus petits.
// var httpHandler = new GrpcWebHandler (GrpcWebMode.GrpcWebText, new HttpClientHandler ());
return GrpcChannel.ForAddress (backendUrl, new GrpcChannelOptions {HttpHandler = httpHandler});
});
Il ne reste plus qu'à définir le tampon de protocole dans le client Blazor puis à créer une page pour afficher les résultats. La définition du tampon de protocole va être une copie exacte de ce que nous avons défini pour notre serveur. Il existe de nombreuses façons différentes de partager ces données entre les clients, je me contente de copier / coller le fichier pour rendre l'exemple un peu plus simple. Vous pouvez tout aussi facilement créer une bibliothèque distincte partagée par les deux projets. Vous pouvez voir où nous avons placé le BeachConditions.proto en consultant le projet ici .
Maintenant, ajoutons notre nouvelle page qui appelle le serveur via gRPC.
@page " / beachconditions "
@inject GrpcChannel Channel
@utilisation de Google.Protobuf.WellKnownTypes
Conditions de la plage
Conditions à différents points de la côte.
@if (beachConditions == null)
{
Chargement des conditions de la plage ...
}
autre
{
BeachId
Nom
Condition
@foreach (var beachCondition in beachConditions)
{
@ beachCondition.BeachId
@ beachCondition.Name
@ beachCondition.Condition
}
}
@code {
IList privé beachConditions;
Tâche async de remplacement protégé OnInitializedAsync ()
{
var client = new Protos.BeachConditions.BeachConditionsClient (Channel);
var getAllBeachConditionsRequest = nouveau Protos.GetAllBeachConditionRequest ();
Protos.GetAllBeachConditionResponse response = attendre client.getAllBeachConditionsAsync (getAllBeachConditionsRequest);
beachConditions = response.BeachConditions;
}
}
Pour référence: BeachConditions.razor .
Si vous ouvrez l'onglet Réseau et ouvrez l'onglet Conditions de plage, vous remarquerez que les données sur l'état de la plage ont été envoyées sous forme de données binaires et affichées. . Bien que la charge utile soit assez petite, vous constaterez de plus grandes améliorations dans les performances à mesure que le nombre de champs augmente en raison des gains de compression que vous recevez en utilisant les tampons de protocole. Données binaires » title= »Beach Conditions page with gRPC binary data in » data-method= »ResizeFitToAreaArguments » data-customsizemethodproperties= »{« MaxWidth »: » », »MaxHeight »: »350″, »ScaleUp »:false, »Quality »: »High »} » data-displaymode= »Custom » data-openoriginalimageonclick= »true »/>
Discutez des améliorations de gRPC pour Blazor et d'autres points de terminaison génériques
À ce stade, vous avez vu à quel point il est facile de faire le changement et vous vous demandez peut-être, " Pourquoi devrais-je faire ce changement maintenant ? " Deux des plus grands avantages de gRPC par rapport aux API REST sont la définition stricte des services / objets et des performances.
Les services basés sur des contrats vous permettent de modéliser vos données avec la certitude que tout ce que vous envoyez / recevez les données sera exactement ce qui est envoyé car il n'y a pas de couche de sérialisation. Dans de nombreuses charges utiles JSON, il existe une couche d'incertitude lors de l'analyse des données et de la validation du fait que certaines données sont analysées dans le type correct. Avec des charges utiles définies, vous saurez que les données sont modélisées correctement.
L'autre avantage supplémentaire est les performances (temps de chargement des données) que vous obtiendrez en réduisant la taille des charges utiles. Avec les tampons de protocole, les données sont sérialisées dans un format binaire qui supprime les données inutiles telles que les noms et les types de champ. Étant donné que les champs protobuf sont définis par leur position, il n’est pas nécessaire d’envoyer des données en double (noms de champ) car l’autre côté connaît la charge utile qu’il reçoit. La réduction de la charge utile variera en fonction de la structure de votre objet, mais plus la charge utile est petite, moins il y a de paquets à envoyer pour terminer la requête.
J'espère que vous êtes encouragé à vous lancer et à commencer à adopter gRPC dans votre projet. Il existe depuis un certain temps et a été largement utilisé dans les communications de serveur à serveur et, à mesure que la prise en charge de la communication navigateur-serveur se développe, il lui ouvre une autre voie. Happy Coding!
Si vous souhaitez consulter le projet complet, consultez-le sur GitHub à ce lien .
Source link