API multiclasse

Se una singola API è particolarmente complessa, potresti volerla implementare da più classi Java. Per fare in modo che classi diverse facciano parte della stessa API, devi:

Ad esempio, le due classi seguenti fanno entrambe parte dell'API tictactoe:

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

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

La configurazione dell'API viene specificata tramite le proprietà dell'annotazione @Api. Tuttavia, per più classi nella stessa API, i requisiti @Api vanno oltre la semplice presenza delle stesse stringhe name e version nell'annotazione @Api per ogni classe. Infatti, la tua API di backend non funzionerà se ci sono differenze nelle configurazioni API specificate nelle proprietà @Api delle classi. Qualsiasi differenza nelle proprietà @Api per le classi in un'API multiclasse comporta una configurazione API "ambigua", che non funzionerà in Cloud Endpoints Frameworks per App Engine.

Esistono diversi modi per creare un'API multiclasse non ambigua:

  • Assicurati manualmente che tutte le classi in una singola API abbiano esattamente le stesse proprietà di annotazione @Api.
  • Utilizza l'eredità delle annotazioni tramite l'eredità Java. In questa eredità, tutte le classi in una singola API ereditano la stessa configurazione API da una classe base comune con annotazione @Api.
  • Utilizza l'eredità delle annotazioni tramite l' @ApiReference annotazione su tutte le classi in una singola API per fare in modo che facciano riferimento alla stessa configurazione API da una classe comune con annotazione @Api.

Utilizzo di @ApiClass per le proprietà che possono variare tra le classi

Per utilizzare questa funzionalità, devi importare quanto segue:

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

Sebbene tutte le proprietà nell'annotazione @Api debbano corrispondere a tutte le classi in un API, puoi anche utilizzare l'annotazione @ApiClass per fornire proprietà che non devono essere esattamente le stesse tra le classi. Ad esempio:

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

dove TicTacToeA limita l'accesso utilizzando una lista consentita di ID client contenente l'ID client consentito e TicTacToeB non limita l'accesso.

Tutte le proprietà fornite dall'annotazione @ApiClass hanno una proprietà equivalente nell'annotazione @Api. Tieni presente che la proprietà equivalente @Api funge da valore predefinito a livello di API. Se esiste un valore predefinito a livello di API per la stessa proprietà, specificato in @Api, la proprietà @ApiClass specifica della classe esegue l'override del valore predefinito a livello di API.

Gli esempi seguenti illustrano l'override delle proprietà @Api da parte degli equivalenti @ApiClass specifici della classe:

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

Eredità delle annotazioni

Le proprietà delle annotazioni @Api e @ApiClass possono essere ereditate da altre classi e le singole proprietà possono essere sostituite tramite l'eredità Java o @ApiReference eredità

Utilizzo dell'eredità Java

Una classe che estende un'altra classe con @Api o @ApiClass annotazioni si comporta come se fosse annotata con le stesse proprietà. Ad esempio:

@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 {  }

Le annotazioni vengono ereditate solo tramite la sottoclasse Java, non tramite l'implementazione dell'interfaccia. Ad esempio:

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

Di conseguenza, non è supportato alcun tipo di eredità multipla delle annotazioni dei framework.

L'eredità funziona anche per @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 {  }

dove TicTacToeBoards eredita il valore della proprietà resource boards da BoardsBase, sostituendo quindi l'impostazione della proprietà resource (scores) nella relativa annotazione @Api. Ricorda che se una classe ha specificato la proprietà resource nell'annotazione @Api, tutte le classi devono specificare la stessa impostazione nell'annotazione @Api; questa tecnica di eredità ti consente di sostituire la proprietà @Api.

Utilizzo dell'eredità @ApiReference

Per utilizzare questa funzionalità, devi importare quanto segue:

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

L'annotazione @ApiReference fornisce un modo alternativo per specificare l'eredità delle annotazioni. Una classe che utilizza @ApiReference per specificare un'altra classe con @Api o @ApiClass annotazioni si comporta come se fosse annotata con le stesse proprietà. Ad esempio:

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

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

Se vengono utilizzate sia l'eredità Java sia @ApiReference, le annotazioni vengono ereditate solo tramite l'annotazione @ApiReference. @Api e @ApiClass annotazioni nella classe ereditata tramite l'eredità Java vengono ignorate. Ad esempio:

@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 {  }

Override della configurazione ereditata

Se erediti la configurazione utilizzando l'eredità Java o @ApiReference, puoi sostituire la configurazione ereditata utilizzando una nuova @Api o @ApiClass annotazione. Vengono sostituite solo le proprietà di configurazione specificate nella nuova annotazione. Le proprietà non specificate vengono comunque ereditate. Ad esempio:

@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 {  }

L'override dell'eredità funziona anche per @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 {  }

L'override funziona anche quando si eredita tramite @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 {  }

Eredità delle annotazioni @ApiMethod

L'annotazione @ApiMethod può essere ereditata dai metodi sostituiti. Ad esempio:

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) {  }
}

Analogamente all'eredità delle annotazioni @Api e @ApiClass, se più metodi che si sostituiscono a vicenda hanno annotazioni @ApiMethod, è possibile sostituire le singole proprietà. Ad esempio:

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) {  }
}

Non esiste un'annotazione @ApiReference o equivalente per i metodi, quindi @ApiMethod viene sempre ereditata tramite l'eredità Java, non tramite @ApiReference.

Regole di eredità e precedenza

Per riassumere la discussione precedente, la tabella seguente mostra le regole di eredità e l'ordine di precedenza.

Annotazione/eredità Regola
@Api Deve essere identica per tutte le classi.
@ApiClass Specificata per una classe per sostituire le proprietà @Api.
Eredità Java La classe eredita @Api e @ApiClass della classe base.
@ApiReference La classe eredita @Api e @ApiClass della classe a cui viene fatto riferimento.
Utilizzo di @ApiReference in una classe (Java) che eredita da una classe base La classe eredita @Api e @ApiClass della classe a cui viene fatto riferimento, non dalla classe base.

Casi d'uso comuni per l'eredità delle annotazioni

Di seguito sono riportati esempi dei casi d'uso tipici per l'eredità:

Per il controllo della versione dell'API:

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

Per le API multiclasse:

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

Per testare versioni diverse della stessa 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;
  }
}

dove someMethod potrebbe restituire risposte predeterminate, evitare chiamate con effetti collaterali, saltare una richiesta di rete o datastore e così via.

Aggiunta delle classi a web.xml

Dopo aver annotato le classi, devi aggiungerle al file web.xml. L'esempio seguente mostra una singola classe:

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

Per aggiungere più classi:

  1. Sostituisci <param-value>com.example.skeleton.MyApi</param-value> con il nome della tua classe API.

  2. Aggiungi ogni classe all'interno dello stesso campo <param-value> separato da una virgola, ad esempio:

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