HTTP/JSON zu gRPC transcodieren

Cloud Endpoints unterstützt die Protokolltranscodierung, sodass Clients mit HTTP/JSON auf Ihre gRPC API zugreifen können. Der Extensible Service Proxy (ESP) transcodiert HTTP/JSON zu gRPC.

In diesem Leitfaden erfahren Sie:

  • wie Sie Annotationen in der .proto-Datei verwenden, um eine Datenumwandlung von HTTP/JSON in gRPC anzugeben,
  • wie Sie den Dienst in Endpoints bereitstellen, um dieses Feature zu nutzen und
  • wo Sie weitere Referenzinformationen zum Design und Implementieren der Transcodierung für gRPC-Dienste finden.

Dabei wird davon ausgegangen, dass Sie unsere Anleitungen für gRPC bereits durchgearbeitet haben und mit grundlegenden Konzepten von Endpoints für gRPC APIs vertraut sind.

Transcodierungstaugliche API entwerfen

Bei der Transcodierung werden die HTTP-/JSON-Anfragen und deren Parameter den gRPC-Methoden und deren Parametern und Rückgabetypen zugeordnet. Obwohl eine HTTP-/JSON-Anfrage jeder beliebigen API-Methode zugeordnet werden kann, fällt diese Zuordnung leichter, wenn die gRPC API genau wie eine herkömmliche HTTP REST API ressourcenorientiert strukturiert ist. Sie entwerfen den API-Dienst also so, dass er mithilfe einer kleinen Anzahl von Standardmethoden, die HTTP-Verben wie GET und PUT entsprechen, Vorgänge für die im Dienst enthaltenen Ressourcen und Sammlungen von Ressourcen ausführt, die selbst eine Art von Ressource sind. Diese Standardmethoden sind List, Get, Create, Update und Delete.

Gegebenenfalls kann die API auch einige benutzerdefinierte Methoden enthalten, auch wenn es schwieriger ist, diese zuzuordnen.

In der API-Designanleitung finden Sie viele weitere Informationen zum ressourcenorientierten Design und zu den Standardzuordnungen für die Transcodierung. Diese Designanleitung ist der Designstandard, der Google-intern zum Entwerfen öffentlicher APIs wie Cloud APIs verwendet wird. Sie müssen dieser Anleitung zum Verwenden der gRPC-Transcodierung zwar nicht folgen, es wird jedoch dringend empfohlen. Insbesondere werden Ihnen die folgenden Seiten dabei helfen, diese Designgrundsätze zu verstehen und Ihre Methoden um nützliche Transcodierungszuordnungen zu ergänzen:

Die folgende Referenzseite könnte ebenfalls nützlich sein:

Im weiteren Verlauf dieses Dokuments verwenden Sie wieder das Bookstore-Beispiel aus unseren Anleitungen, in dem diese Grundsätze bereits umgesetzt werden. Der Bookstore hat "Shelf"-Sammlungen mit "Book"-Ressourcen, für die Nutzer List-, Get-, Create- oder Delete-Vorgänge ausführen können.

Wo kann die Transcodierung konfiguriert werden

Die gRPC-Transcodierung ist standardmäßig aktiviert, sodass Sie sie ohne jegliche Konfiguration verwenden können. Folgen Sie der Anleitung zum Bereitstellen eines Dienstes mithilfe von Transcodierung. Wenn Sie anschließend eine HTTP-POST-Anfrage an den URL-Pfad GRPC_SERVICE_FULL_NAME/METHOD_NAME> senden und gegebenenfalls Feldwerte für die Anfragenachricht der Methode als JSON im HTTP-Anfragetext angeben, leitet der ESP die Anfragenachricht an die entsprechende gRPC-Methode weiter. Im vorherigen Beispiel ist GRPC_SERVICE_FULL_NAME der vollständige Name Ihres gRPC-Dienstes und METHOD_NAME der Name der Methode.

Wenn Sie beispielsweise diese POST-Anfrage an die Bookstore-URL ListShelves senden:

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

erhalten Sie eine aktuelle Liste der Shelves im JSON-Format.

Die explizite Konfiguration von Zuordnungen ist jedoch im Hinblick auf das HTTP-Schnittstellendesign deutlich zu bevorzugen, wie im weiteren Verlauf dieses Dokuments beschrieben.

Mit dem gRPC API-Konfigurationsstandard für die Dienstkonfiguration können Sie genau festlegen, wie Daten von HTTP/JSON in gRPC zu übersetzen sind. Dabei werden zwei Mechanismen unterstützt: direkte Annotationen in der .proto-Datei und in YAML als Teil der gRPC API-Konfigurationsdatei. Zur leichteren Lesbarkeit und Verwaltung empfehlen wir proto-Annotationen. Wie die YAML-Konfiguration genau aussieht und wann Sie sie möglicherweise verwenden müssen, finden Sie unter Transcodierung in YAML konfigurieren.

Im folgenden Beispiel wird der empfohlene Ansatz im Bookstore verwendet:

// 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;
}

Die Annotation teilt dem ESP mit, dass bei einer HTTP-GET-Anfrage mit der URL http://mydomain/v1/shelves/1 die Methode GetShelf() des gRPC-Servers aufgerufen wird. Der dafür verwendete Parameter GetShelfRequest enthält die angeforderte Shelf-ID shelf (in diesem Fall 1).

Transcodierungszuordnungen hinzufügen

In diesem Abschnitt werden einige zusätzliche Zuordnungsannotationen aus dem Bookstore-Beispiel beschrieben. Das Bookstore-Beispiel enthält zwei proto-Beispieldateien, sodass Sie es mit und ohne Transcodierungszuordnungen bereitstellen und die Unterschiede in den proto-Dateien vergleichen können:

Eine ausführlichere Anleitung zum Festlegen von Transcodierungszuordnungen finden Sie unter Standardmethoden, Benutzerdefinierte Methoden und in der HTTP-Regelreferenz.

Methode List zuordnen

Die Methode List wird in der .proto-Datei mit zugehörigem Antworttyp definiert:

  // 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;
}

Die fett markierte Annotation legt die HTTP-Zuordnung für diese Methode fest.

  • option (google.api.http) gibt an, dass diese Methode eine Annotation für die gRPC-zu-HTTP-Zuordnung ist.
  • get gibt an, dass diese Methode einer HTTP-GET-Anfrage zugeordnet ist.
  • "/v1/shelves" ist die URL-Pfadvorlage, die an die Domain Ihres Dienstes angehängt ist und von der GET-Anfrage verwendet wird, um diese Methode aufzurufen. Der URL-Pfad wird auch als Ressourcenpfad bezeichnet, da er normalerweise die Ressource angibt, die Sie verwenden möchten. In diesem Fall sind dies alle Shelf-Ressourcen des Bookstores.

Wenn ein Client beispielsweise diese Methode durch Senden einer GET-Anfrage an die URL http://mydomain/v1/shelves aufruft, ruft der ESP die gRPC-Methode ListShelves() auf. Das gRPC-Back-End gibt dann die Shelves zurück, die der ESP in das JSON-Format umwandelt und an den Client zurückgibt.

Methode Get zuordnen

Die Methode GetShelf des Bookstores ist in der .proto-Datei mit zugehörigen Anfrage- und Antworttypen definiert:

// 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;
}

Die fett markierte Annotation legt die HTTP-Zuordnung für diese Methode fest.

  • option (google.api.http) gibt an, dass diese Methode eine Annotation für die gRPC-zu-HTTP-Zuordnung ist.
  • get gibt an, dass diese Methode einer HTTP-GET-Anfrage zugeordnet ist.
  • "/v1/shelves/{shelf}" ist wie oben der URL-Pfad für die Anfrage, gibt jedoch /v1/shelves/ und dann {shelf} an. Diese Notation mit geschweiften Klammern weist den ESP an, alle in {shelf} enthaltenen Elemente als Wert für shelf im Methodenparameter GetShelfRequest bereitzustellen.

Wenn ein Client diese Methode durch Senden einer GET-Anfrage an die URL http://mydomain/v1/shelves/4 aufruft, erstellt der ESP einen GetShelfRequest-Parameter mit einem shelf-Wert von 4 und ruft dann damit die gRPC-Methode GetShelf() auf. Das gRPC-Back-End gibt anschließend das angeforderte Shelf mit der ID 4 zurück, das vom ESP in das JSON-Format umgewandelt und an den Client zurückgegeben wird.

Bei dieser Methode muss der Client nur einen einzigen Anfragefeldwert (shelf) bereitstellen, der in der URL-Pfadvorlage in geschweiften Klammern angegeben wird ("Aufnahme"-Notation). Sie können auch mehrere Teile der URL aufnehmen, wenn das zum Identifizieren der angeforderten Ressource nötig ist. Beispielsweise muss der Client bei der Methode GetBook sowohl die Shelf-ID als auch die Book-ID in der URL angeben:

// 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;
}

Neben Literalen und geschweiften Klammern für Feldwerte können URL-Pfadvorlagen auch Platzhalter enthalten. Damit geben Sie an, dass alle Elemente in diesem Teil der URL aufgenommen werden sollen. Die im vorherigen Beispiel verwendete {shelf}-Notation ist eigentlich ein Kürzel für {shelf=*}. Weitere Informationen zu den Regeln für Pfadvorlagen finden Sie in der HTTP-Regelreferenz.

Bei diesem Methodentyp ist kein HTTP-Anfragetext angegeben. Weitere Anleitungen zum Zuordnen von Get-Methoden, darunter zum Verwenden von Abfrageparametern, finden Sie unter Standardmethoden.

Methode Create zuordnen

Die Methode CreateShelf des Bookstores ist HTTP-POST zugeordnet:

  // 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) gibt an, dass diese Methode eine Annotation für die gRPC-zu-HTTP-Zuordnung ist.
  • post gibt an, dass diese Methode einer HTTP-POST-Anfrage zugeordnet ist.
  • "/v1/shelves" ist wie oben der URL-Pfad für die Anfrage.
  • body: "shelf" wird im HTTP-Anfragetext verwendet, um die hinzuzufügende Ressource im JSON-Format anzugeben.

Wenn ein Client beispielsweise diese Methode so aufruft:

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

erstellt der ESP anhand des JSON-Textes einen Shelf-Wert mit dem Thema "Music" für den CreateShelfRequest-Parameter und ruft dann die gRPC-Methode CreateShelf() auf. Dabei stellt der Client keinen id-Wert für Shelf bereit. Die Shelf-IDs des Bookstores werden vom Dienst bereitgestellt, wenn ein neues Shelf erstellt wird. Diese Art von Information stellen Sie Nutzern Ihres Dienstes in der API-Dokumentation zur Verfügung.

Platzhalter im Text verwenden

Mit dem speziellen Namen ** kann in der Textzuordnung angezeigt werden, dass jedes Feld, das nicht durch die Pfadvorlage gebunden ist, dem Anfragetext zugeordnet werden soll. Dadurch wird die folgende alternative Definition der Methode CreateShelf aktiviert.

  // 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) gibt an, dass diese Methode eine Annotation für die gRPC-zu-HTTP-Zuordnung ist.
  • post gibt an, dass diese Methode einer HTTP-POST-Anfrage zugeordnet ist.
  • "/v1/shelves/{shelf_id}" ist der URL-Pfad für die Anfrage. Alles, was sich in {shelf_id} befindet, ist der Wert des Felds shelf_id in CreateShelfRequest.
  • body: "*" wird im HTTP-Anfragetext verwendet, um alle verbleibenden Anfragefelder mit Ausnahme von shelf_id in diesem Beispiel anzugeben. In diesem Beispiel sind es shelf_theme und shelf_size. Die Werte in allen Feldern im JSON-Text mit diesen beiden Namen werden in den entsprechenden Feldern von CreateShelfRequest verwendet.

Wenn ein Client beispielsweise diese Methode so aufruft:

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

Der ESP verwendet den JSON-Text und die Pfadvorlage, um eine CreateShelfRequest{shelf_id: 123 shelf_theme: "Music" shelf_size: 20} zu erstellen und anschließend zum Aufrufen der gRPC-Methode CreateShelf() zu verwenden. Weitere Informationen finden Sie unter HttpRule.

Transcodierung in YAML konfigurieren

Ein alternativer Ansatz besteht darin, die HTTP-zu-gRPC-Zuordnungen in der YAML-Datei für die gRPC API-Konfiguration und nicht in der .proto-Datei anzugeben. Sie müssen die Transcodierung gegebenenfalls in einer YAML-Datei konfigurieren, wenn Sie eine einzelne proto-API-Definition haben, die in mehreren Diensten mit unterschiedlichen Zuordnungen für jeden Dienst verwendet wird.

Die rules im Abschnitt http der YAML-Datei legen fest, wie HTTP-/JSON-Anfragen den gRPC-Methoden zugeordnet werden:

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}
  ...

Ein ausführlicheres Anwendungsbeispiel für diesen Ansatz finden Sie unter api_config_http.yaml am Beispiel des Bookstore-Dienstes.

Dienst mit Transcodierung bereitstellen

Ein gRPC-Dienst, der Transcodierung verwendet, wird mit einem wesentlichen Unterschied größtenteils wie jeder andere gRPC-Dienst bereitgestellt. In den Anleitungen musste der Beispiel-Bookstore nur gRPC-Anfragen vom Beispielclient annehmen. Wenn der Bookstore jedoch auch HTTP-Anfragen annehmen soll, müssen Sie den ESP dafür speziell konfigurieren. Clients verwenden das Protokoll HTTP1.1, um JSON-/HTTP-Anfragen an den ESP zu senden. Somit muss für den ESP entweder SSL konfiguriert (der SSL-Port unterstützt beide Anfragetypen) oder ein spezieller Port zur Annahme dieser Aufrufe aktiviert werden. Ansonsten erfolgt die Bereitstellung weitgehend so, wie sie in der Anleitung für Ihre Umgebung beschrieben ist.

Bereitstellung von HTTP-Regeln sicherstellen

Wenn Sie das Bookstore-Beispiel bereits für die Anleitungen heruntergeladen haben, müssen Sie noch eine etwas andere Version der .proto-Datei mit Annotationen (http_bookstore.proto) herunterladen. Außerdem müssen Sie vor der protoc-Ausführung das Repository googleapis aus GitHub klonen, da Sie annotations.proto im Include-Pfad benötigen:

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

    GOOGLEAPIS_DIR=<your-local-googleapis-folder>

Anschließend erstellen Sie einen neuen .pb-Deskriptor aus http_bookstore.proto, wenn Sie Ihre Konfiguration in Endpoints bereitstellen:

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

Wenn Sie die alternative Methode zum Konfigurieren von HTTP-Zuordnungen in der YAML-Datei für die gRPC API-Konfiguration verwenden, müssen Sie beim Bereitstellen der Konfiguration in Endpoints auch darauf achten, dass die relevanten Regeln vorhanden sind. Bei Verwendung dieser Methode mit dem Bookstore-Dienst geben Sie dafür die Datei api_config.yaml mit den grundlegenden Regeln und die Datei api_config_http.yaml mit den HTTP-Regeln an:

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

SSL verwenden

Wenn SSL für die Kommunikation zwischen den Clients und dem ESP aktiviert ist, können Clients denselben Port für gRPC- und HTTP1.1-Aufrufe verwenden. Informationen zum Einrichten von SSL für einen Endpoints-Dienst finden Sie unter SSL aktivieren.

Mit dem Flag --ssl_port können Sie in der Konfigurationsdatei für Google Kubernetes Engine (GKE) oder im Befehl docker run (Compute Engine/Docker) einen Port für den ESP angeben, um SSL-Aufrufe anzunehmen:

    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",
    ]

Port HTTP1.1 einrichten

Wenn Sie kein SSL verwenden, müssen Sie einen eigenen Port für HTTP1.1-Anfragen einrichten, da gRPC und HTTP1.1 ohne SSL nicht denselben Port gemeinsam nutzen können. Verwenden Sie das Flag --http_port in der GKE-Konfigurationsdatei oder im Befehl docker run, um einen Port für die Annahme von HTTP1.1-Aufrufen anzugeben. Falls der ESP auch gRPC-Aufrufe annehmen soll, müssen Sie außerdem mit dem Flag --http2_port einen gRPC-Port angeben,

    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",
    ]

Dienst mit Transkodierung aufrufen

In diesem Abschnitt wird beschrieben, wie Sie einen Dienst einrichten und HTTP-Aufrufe an den Dienst senden.

Dienst einrichten

In diesem Abschnitt wird davon ausgegangen, dass Sie die grundlegenden Anleitungen zu gRPC-Diensten für Ihre Umgebung bereits durchgearbeitet und einen GKE-Cluster oder eine Compute Engine-Instanz zum Ausführen des Beispiels eingerichtet haben.

  1. Prüfen Sie zuerst, ob Sie die HTTP-taugliche Bookstore-Dienstkonfiguration in Endpoints bereitgestellt haben, wie unter Bereitstellung von HTTP-Regeln gewährleisten beschrieben.
  2. Stellen Sie das Backend und den ESP bereit, wie in der Anleitung für Ihre Plattform beschrieben. Geben Sie dazu das Flag --http_port an, um einen Port für HTTP1.1-Anfragen zu aktivieren:

HTTP-Aufrufe an den Dienst senden

  1. Rufen Sie die externe IP-Adresse des ESP ab und legen Sie sie auf $ESP_IP fest.
  2. Senden Sie die folgende HTTP-Anfrage mit curl

    curl http://$ESP_IP/v1/shelves
    

    (oder verwenden Sie dieselbe URL mit https://, wenn Sie SSL verwenden). Der Server antwortet mit:

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

    Wenn eine binäre Antwort ausgegeben wird, prüfen Sie Ihre Portkonfiguration. Möglicherweise greifen Sie auf den HTTP2-Port und nicht den HTTP-Port zu.

  3. Versuchen Sie es mit einer Create-Methode. CreateShelf setzt einen API-Schlüssel voraus, sodass Sie für Ihr Projekt einen Schlüssel erstellen und als $KEY festlegen müssen. Rufen Sie jetzt Folgendes auf:

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

    Wenn Sie GetShelves noch einmal aufrufen, sollten Sie das neue Shelf sehen.