APIs multiclase

Si una API es especialmente compleja, puede que quieras implementarla desde varias clases de Java. Para que diferentes clases formen parte de la misma API, debes hacer lo siguiente:

Por ejemplo, las dos clases siguientes forman parte de la API tictactoe:

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

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

La configuración de la API se especifica mediante las propiedades de anotación @Api. Sin embargo, en el caso de varias clases en la misma API, los requisitos de @Api van más allá de tener las mismas cadenas name y version en la anotación @Api de cada clase. De hecho, tu API de backend no funcionará si hay alguna diferencia en las configuraciones de la API especificadas en las propiedades @Api de las clases. Si hay alguna diferencia en las propiedades @Api de las clases de un resultado de API de varias clases, la configuración de la API será ambigua y no funcionará en Cloud Endpoints Frameworks para App Engine.

.

Hay varias formas de crear una API multiclass inequívoca:

  • Asegúrate manualmente de que todas las clases de una misma API tengan exactamente las mismas propiedades de anotación @Api.
  • Usa la herencia de anotaciones mediante la herencia de Java. En esta herencia, todas las clases de una sola API heredan la misma configuración de API de una clase base común anotada con @Api.
  • Usa la herencia de anotaciones a través de la anotación @ApiReference en todas las clases de una misma API para que hagan referencia a la misma configuración de la API desde una clase común con la anotación @Api.

Usar @ApiClass para propiedades que pueden variar entre clases

Para usar esta función, necesitas la siguiente importación:

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

Aunque todas las propiedades de la anotación @Api deben coincidir en todas las clases de una API, también puedes usar la anotación @ApiClass para proporcionar propiedades que no tengan que ser exactamente iguales entre clases. Por ejemplo:

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

donde TicTacToeA limita el acceso mediante una lista blanca de IDs de cliente que contiene el ID de cliente permitido, y TicTacToeB no limita el acceso.

Todas las propiedades proporcionadas por la anotación @ApiClass tienen una propiedad equivalente en la anotación @Api. Ten en cuenta que la propiedad equivalente @Api actúa como valor predeterminado en toda la API. Si hay un valor predeterminado para toda la API para esa misma propiedad, especificado en @Api, la propiedad @ApiClass específica de la clase anula el valor predeterminado de toda la API.

En los siguientes ejemplos se muestra cómo se anulan las propiedades @Api mediante los equivalentes @ApiClass específicos de la clase:

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

Heredados de la anotación

Las propiedades de anotación @Api y @ApiClass se pueden heredar de otras clases, y las propiedades individuales se pueden anular mediante la herencia de Java o la herencia de @ApiReference.

Usar la herencia de Java

Una clase que amplía otra clase con anotaciones @Api o @ApiClass se comporta como si tuviera las mismas propiedades. Por ejemplo:

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

Las anotaciones solo se heredan a través de las subclases de Java, no a través de la implementación de interfaces. Por ejemplo:

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

Por lo tanto, no se admite ningún tipo de herencia múltiple de anotaciones de frameworks.

La herencia también funciona con @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 {  }

donde TicTacToeBoards hereda el valor de la propiedad resource (boards) de BoardsBase, lo que anula la configuración de la propiedad resource (scores) en su anotación @Api. Recuerda que, si alguna clase ha especificado la propiedad de recurso en la anotación @Api, todas las clases deben especificar el mismo ajuste en la anotación @Api. Esta técnica de herencia te permite anular la propiedad @Api.

Usar la herencia de @ApiReference

Para usar esta función, necesitas la siguiente importación:

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

La anotación @ApiReference proporciona una forma alternativa de especificar la herencia de anotaciones. Una clase que usa @ApiReference para especificar otra clase con anotaciones @Api o @ApiClass se comporta como si tuviera las mismas propiedades. Por ejemplo:

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

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

Si se usan tanto la herencia de Java como @ApiReference, las anotaciones se heredarán solo a través de la anotación @ApiReference. Se ignoran las anotaciones @Api y @ApiClass de la clase heredada a través de la herencia de Java. Por ejemplo:

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

Anular la configuración heredada

Tanto si heredas la configuración mediante la herencia de Java como si lo haces con @ApiReference, puedes anular la configuración heredada usando una nueva anotación @Api o @ApiClass. Solo se anulan las propiedades de configuración especificadas en la nueva anotación. Las propiedades que no se especifican se siguen heredando. Por ejemplo:

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

La anulación de la herencia también funciona con @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 {  }

La anulación también funciona cuando se hereda a través de @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 {  }

Heredar anotaciones de @ApiMethod

La anotación @ApiMethod se puede heredar de los métodos anulados. Por ejemplo:

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

Al igual que con la herencia de anotaciones @Api y @ApiClass, si varios métodos que se sobrescriben entre sí tienen anotaciones @ApiMethod, se pueden sobrescribir propiedades individuales. Por ejemplo:

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

No hay ninguna anotación @ApiReference ni equivalente para los métodos, por lo que @ApiMethod siempre se hereda a través de la herencia de Java, no a través de @ApiReference.

Reglas de herencia y precedencia

Para resumir la conversación anterior, en la siguiente tabla se muestran las reglas de herencia y el orden de precedencia.

Anotación o herencia Regla
@Api Debe ser idéntico en todas las clases.
@ApiClass Se especifica para una clase con el fin de anular las propiedades de @Api.
Herencia de Java La clase hereda @Api y @ApiClass de la clase base.
@ApiReference Class hereda @Api y @ApiClass de la clase a la que se hace referencia.
Usar @ApiReference en una clase (Java) que hereda de una clase base La clase hereda @Api y @ApiClass de la clase a la que se hace referencia, no de la clase base.

Casos de uso habituales de la herencia de anotaciones

A continuación se muestran ejemplos de los casos prácticos habituales de la herencia:

Para el control de versiones de la API:

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

En el caso de las APIs multiclase:

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

Para probar diferentes versiones de la misma API, sigue estos pasos:

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

donde someMethod puede devolver respuestas predeterminadas, evitar llamadas con efectos secundarios, omitir una solicitud de red o de almacén de datos, etc.

Añadir las clases a web.xml

Después de anotar las clases, debes añadirlas al archivo web.xml. En el siguiente ejemplo se muestra una sola clase:

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

Para añadir varias clases, sigue estos pasos:

  1. Sustituye <param-value>com.example.skeleton.MyApi</param-value> por el nombre de tu clase de API.

  2. Añade cada clase dentro del mismo campo <param-value> separada por una coma. Por ejemplo:

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