Transcoder du contenu HTTP/JSON en gRPC

Cloud Endpoints accepte le transcodage de protocole afin que les clients puissent accéder à votre API gRPC à l'aide de HTTP/JSON. Extensible Service Proxy (ESP) transcode le protocole HTTP/JSON en gRPC.

Ce guide vous explique :

  • comment utiliser des annotations dans votre fichier .proto pour spécifier la conversion de données HTTP/JSON en gRPC ;
  • comment déployer votre service dans Endpoints pour utiliser cette fonctionnalité ;
  • où trouver plus d'informations de référence sur la conception et la mise en œuvre du transcodage pour les services gRPC.

Nous partons ici du principe que vous avez déjà terminé nos tutoriels gRPC et que vous maîtrisez les concepts de base de Endpoints pour les API gRPC.

Concevoir une API permettant le transcodage

Le transcodage implique de mapper des requêtes HTTP/JSON et leurs paramètres sur les méthodes gRPC et leurs propres paramètres et types de renvois. De ce fait, s'il est possible de mapper une requête HTTP/JSON sur n'importe quelle méthode API arbitraire, cela est facilité si l'API gRPC est elle-même structurée de manière à être orientée ressources, tout comme une API REST HTTP traditionnelle. En d'autres termes, vous devez concevoir le service API de manière à ce qu'il utilise un petit nombre de méthodes standards correspondant à des verbes HTTP tels que GET et PUT, qui opèrent sur les ressources du service et sur des collections de ressources, qui constituent elles-mêmes un type de ressource. Ces méthodes standards sont List, Get, Create, Update et Delete.

Si nécessaire, l'API peut également comporter des méthodes personnalisées non standards, bien que celles-ci ne soient pas aussi simples à mapper.

Pour plus d'informations sur la conception orientée ressources et les mappages de transcodage standards, consultez notre guide de conception d'API. Il s'agit de la norme suivie par Google lors de la conception d'API publiques telles que les API Cloud. Bien que vous n'ayez pas besoin de suivre ce guide pour utiliser le transcodage gRPC, nous vous le recommandons vivement. En particulier, les pages suivantes vous aideront à comprendre ces principes de conception et à ajouter des mappages de transcodage utiles à vos méthodes :

La page de référence suivante peut également être utile :

Dans la suite de ce document, vous vous servirez de l'exemple Bookstore que vous avez utilisé dans nos tutoriels, qui repose déjà sur ces principes. La librairie dispose de collections "shelf" (étagère) de ressources "book" (livre), que les utilisateurs peuvent répertorier (List), récupérer (Get), créer (Create) ou supprimer (Delete).

Où configurer le transcodage

Le transcodage gRPC est activé par défaut et vous pouvez l'utiliser sans configuration de votre part. Suivez les instructions pour déployer un service utilisant le transcodage. Ensuite, lorsque vous envoyez une requête HTTP POST au chemin d'URL GRPC_SERVICE_FULL_NAME/METHOD_NAME> avec, pour le champ de message de requête correspondant à la méthode, des valeurs (le cas échéant) sous forme de contenus JSON dans le corps de la requête HTTP, ESP envoie le message de requête à la méthode gRPC appropriée. Dans l'exemple précédent, GRPC_SERVICE_FULL_NAME est le nom complet de votre service tandis que METHOD_NAME est le nom de la méthode.

Par exemple, si vous envoyez un POST à l'URL ListShelves de Bookstore comme suit :

curl -XPOST http://mydomain/endpoints.examples.bookstore.Bookstore/ListShelves

… vous devriez obtenir une liste à jour des étagères au format JSON.

Cependant, en termes de conception d'interface HTTP, il est nettement préférable de configurer explicitement les mappages, comme décrit dans la suite de ce document.

La norme de configuration de l'API gRPC pour la configuration de service vous permet de spécifier exactement comment les données HTTP/JSON doivent être converties en gRPC. Deux mécanismes sont disponibles pour cela : les annotations directes du fichier .proto et, en YAML, le paramétrage de votre fichier de configuration de l'API gRPC. Nous vous recommandons d'utiliser les annotations du fichier proto, ce qui facilite la lecture et la maintenance. Pour plus d'informations sur la configuration YAML et sur les cas où vous devrez peut-être l'utiliser, consultez la section Configurer le transcodage en YAML.

Voici un exemple issu de Bookstore qui est basé sur l'approche que nous recommandons :

// Returns a specific bookstore shelf.
rpc GetShelf(GetShelfRequest) returns (Shelf) {
  // Client example - returns the first shelf:
  //   curl http://DOMAIN_NAME/v1/shelves/1
  option (google.api.http) = { get: "/v1/shelves/{shelf}" };
}

...
// Request message for GetShelf method.
message GetShelfRequest {
  // The ID of the shelf resource to retrieve.
  int64 shelf = 1;
}

L'annotation indique à ESP qu'une requête HTTP GET avec l'URL http://mydomain/v1/shelves/1 appelle la méthode GetShelf() du serveur gRPC, avec un GetShelfRequest contenant l'ID d'étagère demandé shelf (dans ce cas, 1).

Ajouter des mappages de transcodage

Cette section décrit quelques annotations de mappage supplémentaires, toujours en se basant sur l'exemple Bookstore. L'exemple Bookstore contient deux exemples de fichiers proto afin que vous puissiez le déployer avec ou sans les mappages de transcodage et comparer les différences entre les fichiers proto :

Pour des informations plus complètes sur la spécification de mappages de transcodage, consultez les rubriques Méthodes standards et Méthodes personnalisées, ainsi que les informations de référence relatives aux règles HTTP.

Mapper une méthode List

La méthode List est définie dans le fichier .proto avec son type de réponse :

  // Returns a list of all shelves in the bookstore.
  rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
    // Define HTTP mapping.
    // Client example (Assuming your service is hosted at the given 'DOMAIN_NAME'):
    //   curl http://DOMAIN_NAME/v1/shelves
    option (google.api.http) = { get: "/v1/shelves" };
  }
...
message ListShelvesResponse {
  // Shelves in the bookstore.
  repeated Shelf shelves = 1;
}

L'annotation en gras spécifie le mappage HTTP pour cette méthode.

  • option (google.api.http) indique qu'il s'agit d'une annotation de mappage HTTP/gRPC.
  • get indique que cette méthode est mappée sur une requête GET HTTP.
  • "/v1/shelves" est le modèle de chemin d'URL (ajouté au domaine de votre service) que la requête GET utilise pour appeler cette méthode. Il est également appelé chemin de ressource, car il spécifie généralement l'objet ou la ressource que vous souhaitez utiliser : dans ce cas, toutes les ressources "shelf" de notre Bookstore.

Ainsi, par exemple, si un client appelle cette méthode en envoyant un GET à l'URL http://mydomain/v1/shelves, ESP appelle la méthode gRPC ListShelves(). Le backend gRPC renvoie ensuite les étagères (shelves), qu'ESP convertit au format JSON et renvoie au client.

Mapper une méthode Get

La méthode GetShelf de Bookstore est définie dans le fichier .proto avec ses types de requête et de réponse :

// Returns a specific bookstore shelf.
rpc GetShelf(GetShelfRequest) returns (Shelf) {
  // Client example - returns the first shelf:
  //   curl http://DOMAIN_NAME/v1/shelves/1
  option (google.api.http) = { get: "/v1/shelves/{shelf}" };
}

...
// Request message for GetShelf method.
message GetShelfRequest {
  // The ID of the shelf resource to retrieve.
  int64 shelf = 1;
}
...
// A shelf resource.
message Shelf {
  // A unique shelf id.
  int64 id = 1;
  // A theme of the shelf (fiction, poetry, etc).
  string theme = 2;
}

L'annotation en gras spécifie le mappage HTTP pour cette méthode.

  • option (google.api.http) indique qu'il s'agit d'une annotation de mappage HTTP/gRPC.
  • get indique que cette méthode est mappée sur une requête GET HTTP.
  • "/v1/shelves/{shelf}" est, comme précédemment, le chemin d'URL de la requête, mais le chemin indiqué ici est /v1/shelves/, puis {shelf}. Cette notation entre accolades indique à ESP que tout ce qui se trouve dans {shelf} est la valeur qu'il doit fournir pour shelf dans le paramètre GetShelfRequest de la méthode.

Si un client appelle cette méthode en envoyant un GET à l'URL http://mydomain/v1/shelves/4, ESP crée un GetShelfRequest avec une valeur shelf de 4, puis l'utilise pour appeler la méthode gRPC GetShelf(). Le backend gRPC renvoie ensuite le Shelf demandé ayant l'ID 4, qu'ESP convertit au format JSON et renvoie au client.

Cette méthode ne nécessite qu'une seule valeur de champ de requête devant être fournie par le client (shelf). Nous la spécifions dans le modèle de chemin d'URL avec notre notation de capture entre accolades. Si nécessaire, vous pouvez également capturer plusieurs parties de l'URL pour identifier la ressource demandée. Par exemple, la méthode GetBook nécessite que le client spécifie à la fois l'ID d'étagère (shelf) et l'ID de livre (book) dans l'URL :

// Returns a specific book.
rpc GetBook(GetBookRequest) returns (Book) {
  // Client example - get the first book from the second shelf:
  //   curl http://DOMAIN_NAME/v1/shelves/2/books/1
  option (google.api.http) = { get: "/v1/shelves/{shelf}/books/{book}" };
}
...
// Request message for GetBook method.
message GetBookRequest {
  // The ID of the shelf from which to retrieve a book.
  int64 shelf = 1;
  // The ID of the book to retrieve.
  int64 book = 2;
}

Outre les littéraux et les accolades de capture de valeurs de champ, les modèles de chemin d'URL peuvent faire appel à des caractères génériques pour indiquer que tout élément figurant dans un segment donné de l'URL doit être capturé. La notation {shelf} utilisée dans l'exemple précédent est en réalité une version abrégée de {shelf=*}. Pour en savoir plus sur les règles applicables aux modèles de chemin d'accès, consultez les informations de référence relatives aux règles HTTP.

Dans le cas de ce type de méthode, il n'y a pas de corps de requête HTTP. Vous trouverez davantage de consignes relatives au mappage des méthodes Get (y compris avec des paramètres de requête) dans la rubrique Méthodes standards.

Mapper une méthode Create

La méthode CreateShelf de Bookstore est mappée sur la méthode HTTP POST.

  // Creates a new shelf in the bookstore.
  rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
    // Client example:
    //   curl -d '{"theme":"Music"}' http://DOMAIN_NAME/v1/shelves
    option (google.api.http) = {
      post: "/v1/shelves"
      body: "shelf"
    };
  }
...
// Request message for CreateShelf method.
message CreateShelfRequest {
  // The shelf resource to create.
  Shelf shelf = 1;
}
...
// A shelf resource.
message Shelf {
  // A unique shelf id.
  int64 id = 1;
  // A theme of the shelf (fiction, poetry, etc).
  string theme = 2;
}
  • option (google.api.http) indique qu'il s'agit d'une annotation de mappage HTTP/gRPC.
  • post indique que cette méthode est mappée sur une requête POST HTTP.
  • "/v1/shelves" est le chemin d'URL de la requête, comme précédemment.
  • body: "shelf" est utilisé dans le corps de la requête HTTP pour spécifier, au format JSON, la ressource que vous souhaitez insérer.

Ainsi, par exemple, si un client appelle cette méthode comme ceci :

curl -d '{"theme":"Music"}' http://DOMAIN_NAME/v1/shelves

ESP utilise le corps JSON pour créer une valeur Shelf associée au thème "Music" pour la requête CreateShelfRequest, puis appelle la méthode gRPC CreateShelf(). Le client ne fournit pas la valeur id pour l'objet Shelf. Les ID d'étagère de Bookstore sont fournis par le service lors de la création d'une étagère. C'est le type d'information que vous devez fournir aux utilisateurs de votre service dans la documentation de l'API.

Utiliser un caractère générique dans le corps

Le nom spécial * peut être utilisé dans le mappage du corps pour indiquer que tout champ non lié par le modèle de chemin d'accès doit être mappé au corps de la requête. Ceci active la définition alternative suivante pour la méthode CreateShelf de mise à jour.

  // Creates a new shelf in the bookstore.
  rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
    // Client example:
    //   curl -d '{"shelf_theme":"Music", "shelf_size": 20}' http://DOMAIN_NAME/v1/shelves/123
    option (google.api.http) = {
      post: "/v1/shelves/{shelf_id}"
      body: "*"
    };
  }
...
// Request message for CreateShelf method.
message CreateShelfRequest {
  // A unique shelf id.
  int64 shelf_id = 1;
  // A theme of the shelf (fiction, poetry, etc).
  string shelf_theme = 2;
  // The size of the shelf
  int64 shelf_size = 3;
}
  • option (google.api.http) indique qu'il s'agit d'une annotation de mappage HTTP/gRPC.
  • post indique que cette méthode est mappée sur une requête POST HTTP.
  • "/v1/shelves/{shelf_id}" est le chemin d'URL de la requête. La valeur présente dans {shelf_id} correspond à la valeur du champ shelf_id dans CreateShelfRequest.
  • body: "*" est utilisé dans le corps de la requête HTTP pour spécifier tous les champs de requête restants, à l'exception de shelf_id dans cet exemple, qui sont shelf_theme et shelf_size. Les valeurs de tous les champs du corps JSON contenant ces deux noms seront utilisés dans les champs correspondants de CreateShelfRequest.

Par exemple, si un client appelle cette méthode de la façon suivante :

curl -d '{"shelf_theme":"Music", "shelf_size": 20}' http://DOMAIN_NAME/v1/shelves/123

ESP utilise le corps JSON et le modèle de chemin pour créer un CreateShelfRequest{shelf_id: 123 shelf_theme: "Music" shelf_size: 20}, puis l'utilise pour appeler la méthode gRPC CreateShelf(). Pour plus de détails, consultez la HttpRule.

Configurer le transcodage en YAML

Une approche alternative consiste à spécifier vos mappages HTTP vers gRPC dans le fichier YAML de configuration de l'API gRPC plutôt que dans le fichier .proto. Configurer le transcodage dans un fichier YAML peut être nécessaire si vous avez une seule définition d'API proto utilisée au sein de plusieurs services, des mappages différents étant spécifiés pour chaque service.

Les règles (rules) de la section http de votre fichier YAML indiquent comment mapper des requêtes HTTP/JSON avec des méthodes gRPC :

http:
  rules:
  ...
  #
  # 'GetShelf' is available via the GET HTTP verb and '/shelves/{shelf}' URL
  # path, where {shelf} is the value of the 'shelf' field of 'GetShelfRequest'
  # protobuf message.
  #
  # Client example - returns the first shelf:
  #   curl http://DOMAIN_NAME/v1/shelves/1
  #
  - selector: endpoints.examples.bookstore.Bookstore.GetShelf
    get: /v1/shelves/{shelf}
  ...

Un exemple plus complet basé sur cette approche pour notre exemple de service Bookstore est disponible dans api_config_http.yaml.

Déployer un service utilisant le transcodage

Le déploiement d'un service gRPC utilisant le transcodage est sensiblement identique à celui de tout autre service gRPC, avec une différence majeure. Dans les tutoriels, l'exemple devait uniquement accepter les requêtes gRPC en provenance du client d'exemple. Toutefois, si l'application Bookstore doit également accepter les requêtes HTTP, vous devez compléter la configuration pour ESP. Les clients utilisent le protocole HTTP1.1 pour envoyer des requêtes JSON/HTTP à ESP. Par conséquent, ESP doit être configuré pour utiliser SSL (le port SSL est compatible avec les deux types de requêtes) ou un port spécial doit avoir été activé pour accepter ces requêtes. Le déploiement est par ailleurs sensiblement identique à celui décrit dans le tutoriel de l'environnement choisi.

Vérifier que les règles HTTP sont déployées

Si vous avez déjà téléchargé notre exemple Bookstore pour nos tutoriels, vous devez télécharger une version légèrement différente du fichier .proto comportant des annotations, http_bookstore.proto. Vous devrez également cloner le dépôt googleapis à partir de GitHub avant d’exécuter protoc, car vous aurez besoin d'annotations.proto dans votre chemin d’inclusion.

    git clone https://github.com/googleapis/googleapis

    GOOGLEAPIS_DIR=<your-local-googleapis-folder>

Créez ensuite un descripteur .pb à partir de http_bookstore.proto lors du déploiement de votre configuration sur Endpoints :

    protoc \
        --include_imports \
        --include_source_info \
        --proto_path=${GOOGLEAPIS_DIR} \
        --proto_path=. \
        --descriptor_set_out=api_descriptor.pb \
        http_bookstore.proto

Si vous utilisez la méthode alternative de configuration des mappages HTTP dans votre fichier YAML de configuration de l'API gRPC, vous devez également vous assurer que les règles pertinentes sont mises en place lors du déploiement de votre configuration sur Endpoints. Pour essayer cela avec le service Bookstore, ses règles de base sont dans le fichier api_config.yaml et ses règles HTTP dans le fichier api_config_http.yaml :

    gcloud endpoints services deploy api_descriptor.pb api_config.yaml api_config_http.yaml

Utiliser SSL

Si SSL est activé pour la communication entre vos clients et ESP, les clients peuvent alors effectuer des appels gRPC ou HTTP1.1 sur le même port. Pour savoir comment configurer SSL pour un service Endpoints, consultez la rubrique concernant l'activation de SSL.

Spécifiez un port permettant à ESP d'accepter les appels SSL à l'aide de l'indicateur --ssl_port dans le fichier de configuration de Google Kubernetes Engine (GKE) ou de la commande docker run (Compute Engine/Docker).

    args: [
      "--http_port", "8080",
      "--ssl_port", "443",  # enable SSL port at 443 to serve https requests
      "--backend",  "grpc://127.0.0.1:8081",  # gRPC backend.
      "--service", "SERVICE_NAME",
      "--rollout_strategy", "managed",
    ]

Configurer un port HTTP1.1

Si vous n'utilisez pas SSL, vous devez configurer un port distinct pour les requêtes HTTP1.1 car, sans SSL, gRPC et HTTP1.1 ne peuvent pas partager le même port. Utilisez l'indicateur --http_port dans le fichier de configuration GKE ou la commande docker run pour spécifier un port destiné à recevoir les appels HTTP1.1. Si vous souhaitez également que ESP accepte les appels gRPC, vous devez aussi utiliser l'indicateur --http2_port pour spécifier un port gRPC.

    args: [
      "--http_port", "8080",  # for HTTP 1.1
      "--http2_port", "8090",  # for gRPC
      "--backend", "grpc://127.0.0.1:8081",  # gRPC backend.
      "--service", "SERVICE_NAME",
      "--rollout_strategy", "managed",
    ]

Appeler un service utilisant le transcodage

Cette section décrit la configuration du service et explique comment passer des appels HTTP au service.

Configurer le service

Cette section part du principe que vous avez déjà suivi les tutoriels élémentaires relatifs au service gRPC dans l'environnement de votre choix, et que vous disposez d'un cluster GKE ou d'une instance Compute Engine pour exécuter l'exemple.

  1. Commencez par vérifier que vous avez déployé la configuration du service Bookstore compatible avec HTTP sur Endpoints, comme décrit dans la section Vérifier que les règles HTTP sont déployées.
  2. Déployez le backend et ESP comme décrit dans le tutoriel correspondant à la plate-forme de votre choix en vous servant de l'indicateur --http_port pour activer un port pour les requêtes HTTP1.1 :

Envoyer des appels HTTP au service

  1. Obtenez l'adresse IP externe d'ESP et définissez-la en tant que $ESP_IP.
  2. Envoyez la requête HTTP suivante avec curl

    curl http://$ESP_IP/v1/shelves
    

    (ou utilisez la même URL avec https:// si vous vous servez de SSL). Le serveur doit envoyer la réponse suivante :

    {"shelves":[{"id":"1","theme":"Fiction"},{"id":"2","theme":"Fantasy"}]}
    

    Si la sortie affiche une réponse binaire, vérifiez la configuration du port. Vous accédez peut-être au port HTTP2 au lieu du port HTTP.

  3. Essayez une méthode Create. CreateShelf nécessite une clé API. Vous devez donc créer une clé pour votre projet et la définir en tant que $KEY. Effectuez maintenant l'appel suivant :

    curl -d '{"theme":"Music"}' http://$ESP_IP/v1/shelves?key=$KEY
    

    Si vous appelez à nouveau GetShelves, vous devriez voir votre nouvelle étagère.