API Multiclass

Jika satu API sangat rumit, Anda mungkin ingin mengimplementasikannya dari beberapa class Java. Untuk menjadikan berbagai class 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 ditentukan melalui properti anotasi @Api. Namun, untuk beberapa kelas dalam API yang sama, persyaratan @Api tidak hanya memiliki string name dan version yang sama dalam anotasi @Api untuk setiap kelas. Faktanya, API backend Anda tidak akan berfungsi jika ada perbedaan dalam konfigurasi API yang ditentukan dalam properti @Api class. Perbedaan apa pun dalam properti @Api untuk class dalam API multikelas akan menghasilkan konfigurasi API yang "ambigu", yang tidak akan berfungsi di Cloud Endpoints Frameworks untuk App Engine.

Ada beberapa cara untuk membuat API multikelas yang tidak ambigu:

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

Menggunakan @ApiClass untuk properti yang dapat berbeda antar-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 yang diizinkan dari client ID yang berisi client ID yang diizinkan, dan TicTacToeB tidak membatasi akses.

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

Contoh berikut mengilustrasikan penggantian properti @Api oleh padanan @ApiClass khusus class:

// 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 setiap properti dapat diganti baik 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 diwarisi melalui sub-classing Java, bukan melalui implementasi antarmuka. Contoh:

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

Akibatnya, tidak ada dukungan untuk berbagai jenis pewarisan ganda 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 {  }

dengan TicTacToeBoards mewarisi nilai properti resource boards dari BoardsBase, sehingga menggantikan setelan properti resource (scores) dalam anotasi @Api. Ingatlah 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 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 diwariskan hanya 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 diwarisi

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

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

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

Mirip dengan pewarisan anotasi @Api dan @ApiClass, jika beberapa metode yang saling menggantikan memiliki anotasi @ApiMethod, setiap properti dapat digantikan. 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, jadi @ApiMethod selalu diwarisi melalui pewarisan Java, bukan melalui @ApiReference.

Aturan pewarisan dan prioritas

Untuk meringkas pembahasan 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 dari class dasar.
@ApiReference Class mewarisi @Api dan @ApiClass dari class yang dirujuk.
Menggunakan @ApiReference pada class (Java) yang diwarisi dari class dasar Class mewarisi @Api dan @ApiClass dari class yang dirujuk, 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 multikelas:

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

Untuk menguji berbagai versi 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;
  }
}

di mana someMethod dapat menampilkan respons yang telah ditentukan sebelumnya, hindari panggilan dengan efek samping, lewati permintaan jaringan atau datastore, dan sebagainya.

Menambahkan kelas ke web.xml

Setelah memberi anotasi pada class, 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 di 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>