APIs mit mehreren Klassen

Wenn eine einzelne API besonders komplex ist, wollen Sie sie möglicherweise von mehreren Java-Klassen implementieren. Um derselben API verschiedene Klassen zuzuordnen, müssen Sie

Die folgenden beiden Klassen sind z. B. Teil der tictactoe API:

@Api(name = "tictactoe", version = "v1")
class TicTacToeA { … }

@Api(name = "tictactoe", version = "v1")
class TicTacToeB { … }

Die API-Konfiguration wird durch die Attribute in der Annotation @Api festgelegt. Die Anforderungen @Api für mehrere Klassen in der gleichen API sind allerdings nicht schon durch die gleichen Strings für name und version in der Annotation @Api erfüllt. Die Back-End API funktioniert nicht, wenn in den API-Konfigurationen, die in den Attributen @Api der Klasse festgelegt sind, irgendwelche Abweichungen vorliegen. Jeder Unterschied in den @Api-Attributen von Klassen in einer API mit mehreren Klassen führt zu einer nicht eindeutigen API-Konfiguration, die in Cloud Endpoints Frameworks für App Engine nicht funktioniert.

Eine eindeutige mehrklassige API kann auf mehrere Arten erstellt werden:

  • Stellen Sie manuell sicher, dass alle Klassen einer einzelnen API die exakt gleichen Attribute in der Annotation @Apihaben.
  • Verwenden Sie die Übernahme der Annotation durch Java-Übernahme. Bei dieser Übernahme übernehmen alle Klassen in einer einzelnen API die gleiche API-Konfiguration von einer gemeinsamen @Api-annotierten Standardklasse.
  • Verwenden Sie die Übernahme der Annotation mithilfe der @ApiReference-Annotation für alle Klassen in einer einzelnen API, sodass sie dieselbe API-Konfiguration von einer gemeinsamen @Api-annotierten Klasse beziehen.

@ApiClass für abweichende Attribute in Klassen verwenden

Für diese Funktion ist der folgende Import erforderlich:

import com.google.api.server.spi.config.ApiClass;

Obwohl alle Attribute in der Annotation @Apifür alle Klassen in der API übereinstimmen müssen, können Sie zusätzlich mit der Annotation @ApiClass Attribute bereitstellen, die nicht zwingend übereinstimmen müssen. Beispiel:

// API methods implemented in this class allow only "clientIdA".
@Api(name = "tictactoe", version = "v1")
@ApiClass(clientIds = { "clientIdA" })
class TicTacToeA { … }

// API methods implemented in this class provide unauthenticated access.
@Api(name = "tictactoe", version = "v1")
class TicTacToeB { … }

Dabei beschränkt TicTacToeA den Zugriff mithilfe einer weißen Liste mit Client-IDs mit der zulässigen Client-ID. TicTacToeB beschränkt den Zugriff nicht.

Alle von der Annotation @ApiClass zur Verfügung gestellten Attribute haben ein äquivalentes Attribut in der Annotation @Api. Beachten Sie, dass das @Api-äquivalente Attribut als API-weiter Standard gilt. Wenn es einen API-weiten Standard für dasselbe in @Api angegebene Attribut gibt, überschreibt das klassenspezifische Attribut @ApiClass den API-weiten Standard.

Das folgende Beispiel zeigt, wie die klassenspezifischen @ApiClass-Äquivalente die @Api-Attribute überschreiben:

// For this class "boards" overrides "games".
@Api(name = "tictactoe", version = "v1", resource = "games")
@ApiClass(resource = "boards")
class TicTacToeBoards { … }

// For this class "scores" overrides "games".
@Api(name = "tictactoe", version = "v1", resource = "games")
@ApiClass(resource = "scores")
class TicTacToeScores { … }

// For this class, the API-wide default "games" is used as the resource.
@Api(name = "tictactoe", version = "v1", resource = "games")
class TicTacToeGames { … }

Annotationsübernahme

Die Annotationsattribute @Api und @ApiClass können von anderen Klassen übernommen und einzelne Attribute durch Java-Übernahme oder @ApiReference-Übernahme überschrieben werden.

Java-Übernahme verwenden

Eine Klasse, die eine andere Klasse mit @Api- oder @ApiClass-Annotationen erweitert, verhält sich so, als ob sie mit denselben Attributen annotiert wäre. Beispiel:

@Api(name = "tictactoe", version = "v1")
class TicTacToeBase { … }

// TicTacToeA and TicTacToeB both behave as if they have the same @Api annotation as
// TicTacToeBase
class TicTacToeA extends TicTacToeBase { … }
class TicTacToeB extends TicTacToeBase { … }

Annotationen werden nur durch Java-Subklassen übernommen, nicht durch Schnittstellenimplementierung. Beispiel:

@Api(name = "tictactoe", version = "v1")
interface TicTacToeBase { … }
// Does *not* behave as if annotated.
class TicTacToeA implements TicTacToeBase { … }

Daraus folgt, dass es keine Unterstützung für jegliche Form der mehrfachen Übernahme von Frameworks-Annotationen gibt.

Die Übernahme funktioniert auch für @ApiClass:

@ApiClass(resource = "boards")
class BoardsBase { … }

// TicTacToeBoards behaves as if annotated with the @ApiClass from BoardsBase.
// Thus, the "resource" property will be "boards".
@Api(name = "tictactoe", version = "v1", resource = "scores")
class TicTacToeBoards extends BoardsBase { … }

Dabei übernimmt TicTacToeBoards den resource-Attributwert boards von BoardsBase und überschreibt damit die resource-Attributeinstellung (scores) in der Annotation @Api. Denken Sie daran, dass alle Klassen die gleiche Einstellung in der Annotation @Api angeben müssen, falls eine Klasse das Ressourcenattribut in der Annotation @Api angegeben hat. Mit dieser Methode der Übernahme können Sie das Attribut @Api überschreiben.

@ApiReference-Übernahme verwenden

Für diese Funktion ist der folgende Import erforderlich:

import com.google.api.server.spi.config.ApiReference;

Die Annotation @ApiReference bietet eine Alternative, um die Übernahme der Annotation anzugeben. Eine Klasse, die @ApiReference verwendet, um eine andere Klasse mit @Api- oder @ApiClass-Annotationen anzugeben, verhält sich so, als ob sie mit denselben Attributen annotiert wäre. Beispiel:

@Api(name = "tictactoe", version = "v1")
class TicTacToeBase { … }

// TicTacToeA behaves as if it has the same @Api annotation as TicTacToeBase
@ApiReference(TicTacToeBase.class)
class TicTacToeA { … }

Wenn sowohl die Java-Übernahme als auch @ApiReference verwendet werden, erfolgt die Übernahme nur durch die Annotation @ApiReference. @Api- und @ApiClass-Annotationen für die durch Java-Übernahme übernommene Klasse werden ignoriert. Beispiel:

@Api(name = "tictactoe", version = "v1")
class TicTacToeBaseA { … }
@Api(name = "tictactoe", version = "v2")
class TicTacToeBaseB { … }

// TicTacToe will behave as if annotated the same as TicTacToeBaseA, not TicTacToeBaseB.
// The value of the "version" property will be "v1".
@ApiReference(TicTacToeBaseA.class)
class TicTacToe extends TicTacToeBaseB { … }

Übernommene Konfiguration überschreiben

Unabhängig davon, ob Sie die Konfiguration mithilfe von Java-Übernahme oder @ApiReference übernehmen, können Sie die übernommene Konfiguration mit einer neuen @Api- oder @ApiClass-Annotation überschreiben. Nur in der neuen Annotation angegebene Konfigurationsattribute werden überschrieben. Attribute, die nicht angegeben sind, werden trotzdem übernommen. Beispiel:

@Api(name = "tictactoe", version = "v2")
class TicTacToe { … }

// Checkers will behave as if annotated with name = "checkers" and version = "v2"
@Api(name = "checkers")
class Checkers extends TicTacToe { … }

Das Überschreiben von Übernahmen funktioniert auch für @ApiClass:

@Api(name = "tictactoe", version = "v1")
@ApiClass(resource = "boards", clientIds = { "c1" })
class Boards { … }

// Scores will behave as if annotated with resource = "scores" and clientIds = { "c1" }
@ApiClass(resource = "scores")
class Scores { … }

Das Überschreiben funktioniert auch für Übernahmen mithilfe von @ApiReference:

@Api(name = "tictactoe", version = "v2")
class TicTacToe { … }

// Checkers will behave as if annotated with name = "checkers" and version = "v2"
@ApiReference(TicTacToe.class)
@Api(name = "checkers")
class Checkers { … }

@ApiMethod-Annotationen übernehmen

Die Annotation @ApiMethod kann von überschriebenen Methoden übernommen werden. Beispiel:

class TicTacToeBase {
  @ApiMethod(httpMethod = "POST")
  public Game setGame(Game game) { … }
}
@Api(name = "tictactoe", version = "v1")
class TicTacToe extends TicTacToeBase {
  // setGame behaves as if annotated with the @ApiMethod from TicTacToeBase.setGame.
  // Thus the "httpMethod" property will be "POST".
  @Override
  public Game setGame(Game game) { … }
}

Ähnlich wie bei der Übernahme von @Api- und @ApiClass-Annotationen können einzelne Attribute überschrieben werden, wenn mehrere sich überschreibende Methoden @ApiMethod-Annotationen haben. Beispiel:

class TicTacToeBase {
  @ApiMethod(httpMethod = "POST", clientIds = { "c1" })
  public Game setGame(Game game) { … }
}
@Api(name = "tictactoe", version = "v1")
class TicTacToe extends TicTacToeBase {
  // setGame behaves as if annotated with httpMethod = "GET" and clientIds = { "c1"}.
  @ApiMethod(httpMethod = "GET")
  @Override
  public Game setGame(Game game) { … }
}

Es gibt keine @ApiReference-Annotation oder -Äquivalent für Methoden, also wird @ApiMethod immer durch Java-Übernahme übernommen, nicht durch @ApiReference.

Übernahme- und Vorrangregeln

Zur Zusammenfassung der obigen Diskussion zeigt die folgende Tabelle die Übernahmeregeln und die Reihenfolge des Vorrangs.

Annotation/Übernahme Regel
@Api Muss für alle Klassen gleich sein.
@ApiClass Angegeben für eine Klasse, um @Api-Attribute zu überschreiben.
Java-Übernahme Die Klasse übernimmt @Api und @ApiClass der Standardklasse.
@ApiReference Die Klasse übernimmt @Api und @ApiClass der Referenzklasse.
@ApiReference auf eine Klasse (Java) anwenden, die von einer Standardklasse übernimmt. Klasse übernimmt @Api und @ApiClass der Referenzklasse, nicht der Standardklasse.

Häufige Anwendungsfälle für die Annotationsübernahme

Die folgenden Beispiele zeigen typische Anwendungsfälle für die Übernahme:

Für API-Versionierung:

@Api(name = "tictactoe", version = "v1")
class TicTacToeV1 { … }
@Api(version = "v2")
class TicTacToeV2 extends TicTacToeV1 { … }

Für APIs mit mehreren Klassen:

@Api(name = "tictactoe", version = "v1")
class TicTacToeBase {}
@ApiClass(resource = "boards")
class TicTacToeBoards extends TicTacToeBase { … }
@ApiClass(resource = "scores")
class TicTacToeScores extends TicTacToeBase { … }

Zum Testen verschiedener Versionen der gleichen API:

@Api(name = "tictactoe", version = "v1")
class TicTacToe {
  protected Foo someMethod() {
    // Do something real;
  }

  public Foo getFoo() { … }
}

@Api(version="v1test")
class TicTacToeTest extends TicTacToe {
  protected Foo someMethod() {
    // Stub out real action;
  }
}

Dabei gibt someMethod möglicherweise die vorgegebenen Antworten zurück, verhinderte Aufrufe mit Nebenwirkungen, überspringt eine Netzwerk- oder Datastore-Anfrage usw.

Klassen zu web.xml hinzufügen

Nachdem die Klassen annotiert wurden, müssen sie der Datei web.xml hinzugefügt werden. Das folgende Beispiel enthält eine einzelne Klasse:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!-- Wrap the backend with Endpoints Frameworks v2. -->
    <servlet>
        <servlet-name>EndpointsServlet</servlet-name>
        <servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
        <init-param>
            <param-name>services</param-name>
            <param-value>com.example.skeleton.MyApi</param-value>
        </init-param>
    </servlet>
    <!-- Route API method requests to the backend. -->
    <servlet-mapping>
        <servlet-name>EndpointsServlet</servlet-name>
        <url-pattern>/_ah/api/*</url-pattern>
    </servlet-mapping>
</web-app>

So fügen Sie mehrere Klassen hinzu:

  1. Ersetzen Sie <param-value>com.example.skeleton.MyApi</param-value> durch den API-Klassennamen.

  2. Fügen Sie jede Klasse jeweils durch ein Komma getrennt innerhalb dieses <param-value>-Felds ein. Beispiel:

    <param-value>com.example-company.example-api.Hello,com.example-company.example-api.Goodbye</param-value>