API Multiclass

Jika satu API sangat kompleks, Anda mungkin ingin mengimplementasikannya dari beberapa class Java. Untuk membuat class yang berbeda menjadi bagian dari API yang sama, Anda harus:

Misalnya, dua class berikut adalah bagian dari tictactoe API:

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

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

Konfigurasi API ditetapkan melalui properti anotasi @Api. Namun, untuk beberapa class di API yang sama, persyaratan @Api tidak hanya memiliki string name dan version yang sama dalam anotasi @Api untuk setiap class. Bahkan, API backend Anda tidak akan berfungsi jika ada perbedaan dalam konfigurasi API yang ditetapkan dalam properti @Api class. Setiap perbedaan dalam properti @Api untuk class di API multiclass akan menghasilkan konfigurasi API yang " ambigu", yang tidak akan berfungsi di Framework Cloud Endpoints untuk App Engine.

Ada beberapa cara untuk membuat API multiclass yang jelas:

  • Pastikan secara manual semua class dalam satu API memiliki properti anotasi @Api yang sama persis.
  • Menggunakan pewarisan anotasi melalui pewarisan Java. Dalam pewarisan ini, semua class dalam satu API mewarisi konfigurasi API yang sama dari class dasar beranotasi @Api umum.
  • Gunakan pewarisan anotasi melalui anotasi @ApiReference pada semua class dalam satu API agar mereferensikan konfigurasi API yang sama dari class umum beranotasi @Api.

Menggunakan @ApiClass untuk properti yang dapat berbeda di antara class

Untuk menggunakan fitur ini, Anda memerlukan impor berikut:

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

Meskipun semua properti dalam anotasi @Api harus cocok untuk semua class dalam API, Anda juga dapat menggunakan anotasi @ApiClass untuk menyediakan properti yang tidak harus sama persis di antara class. Contoh:

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

dengan TicTacToeA membatasi akses menggunakan daftar client ID yang diizinkan yang berisi client ID yang diizinkan, dan TicTacToeB tidak membatasi akses.

Semua properti yang diberikan oleh anotasi @ApiClass memiliki properti yang setara dalam anotasi @Api. Perhatikan bahwa properti yang setara dengan @Api berfungsi sebagai default seluruh API. Jika ada default seluruh API untuk properti yang sama, yang ditentukan dalam @Api, properti @ApiClass khusus class akan menggantikan default seluruh API.

Contoh berikut menggambarkan penggantian properti @Api oleh @ApiClass khusus class yang setara:

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

Pewarisan anotasi

Properti anotasi @Api dan @ApiClass dapat diwarisi dari class lain, dan masing-masing properti dapat diganti melalui pewarisan Java atau pewarisan @ApiReference

Menggunakan pewarisan Java

Class yang memperluas class lain dengan anotasi @Api atau @ApiClass berperilaku seolah-olah dianotasi dengan properti yang sama. Contoh:

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

Anotasi hanya diwariskan melalui subclass Java, bukan melalui implementasi antarmuka. Contoh:

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

Oleh karena itu, tidak ada dukungan untuk beberapa pewarisan anotasi framework.

Pewarisan juga berfungsi untuk @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 { … }

tempat TicTacToeBoards mewarisi nilai properti resource boards dari BoardsBase, sehingga mengganti setelan properti resource (scores) dalam anotasi @Api-nya. Perlu diingat bahwa jika ada class yang telah menentukan properti resource dalam anotasi @Api, semua class harus menentukan setelan yang sama dalam anotasi @Api; teknik pewarisan ini memungkinkan Anda mengganti properti @Api tersebut.

Menggunakan pewarisan @ApiReference

Untuk menggunakan fitur ini, Anda memerlukan impor berikut:

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

Anotasi @ApiReference menyediakan cara alternatif untuk menentukan pewarisan anotasi. Class yang menggunakan @ApiReference untuk menentukan class lain dengan anotasi @Api atau @ApiClass akan berperilaku seolah-olah dianotasi dengan properti yang sama. Contoh:

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

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

Jika pewarisan Java dan @ApiReference digunakan, anotasi hanya akan mewarisi melalui anotasi @ApiReference. Anotasi @Api dan @ApiClass pada class yang diwarisi melalui pewarisan Java akan diabaikan. Misalnya:

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

Mengganti konfigurasi yang diwariskan

Baik mewarisi konfigurasi menggunakan pewarisan Java atau @ApiReference, Anda dapat mengganti konfigurasi yang diwarisi menggunakan anotasi @Api atau @ApiClass baru. Hanya properti konfigurasi yang ditentukan dalam anotasi baru yang diganti. Properti yang tidak ditentukan masih diwariskan. Contoh:

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

Mengganti pewarisan juga berfungsi untuk @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 { … }

Mengganti juga berfungsi saat mewarisi melalui @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 { … }

Mewarisi anotasi @ApiMethod

Anotasi @ApiMethod dapat diwarisi dari metode yang diganti. Misalnya:

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

Demikian pula dengan pewarisan anotasi @Api dan @ApiClass, jika beberapa metode yang mengganti satu sama lain memiliki anotasi @ApiMethod, masing-masing properti dapat diganti. Contoh:

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

Tidak ada anotasi @ApiReference atau yang setara untuk metode, sehingga @ApiMethod selalu diwarisi melalui pewarisan Java, bukan melalui @ApiReference.

Aturan turunan dan preseden

Untuk menyintesis diskusi sebelumnya, tabel berikut menunjukkan aturan pewarisan dan urutan prioritas.

Anotasi/pewarisan Aturan
@Api Harus sama untuk semua kelas.
@ApiClass Ditentukan untuk class guna mengganti properti @Api.
Pewarisan Java Class mewarisi @Api dan @ApiClass class dasar.
@ApiReference Class mewarisi @Api dan @ApiClass dari class yang direferensikan.
Menggunakan @ApiReference pada class (Java) yang mewarisi dari class dasar Class mewarisi @Api dan @ApiClass dari class yang direferensikan, bukan dari class dasar.

Kasus penggunaan umum untuk pewarisan anotasi

Berikut adalah contoh kasus penggunaan umum untuk pewarisan:

Untuk pembuatan versi API:

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

Untuk API multiclass:

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

Untuk menguji versi lain dari API yang sama:

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

saat someMethod dapat menampilkan respons yang telah ditentukan sebelumnya, menghindari panggilan dengan efek samping, melewati permintaan jaringan atau datastore, dan sebagainya.

Menambahkan class ke web.xml

Setelah memberikan anotasi ke kelas, Anda harus menambahkannya ke file web.xml. Contoh berikut menunjukkan satu class:

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

Untuk menambahkan beberapa kelas:

  1. Ganti <param-value>com.example.skeleton.MyApi</param-value> dengan nama class API Anda sendiri.

  2. Tambahkan setiap class dalam kolom <param-value> yang sama ini yang dipisahkan dengan koma, misalnya:

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