多重類別 API

如果單一的 API 特別複雜,您可能希望從多個 Java 類別實作 API。如要使不同的類別成為相同 API 的一部分,您必須:

例如,下列兩個類別都會成為 tictactoe API 的一部分:

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

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

API 設定是透過 @Api 註解屬性指定。但是對於同一個 API 的多個類別,@Api 需求則會擴展,不僅在 @Api 註解中為每個類別提供相同的 nameversion 字串。實際上,如果在類別 @Api 屬性中指定的 API 設定出現「任何」差異,則後端 API 將無法運作。多重類別 API 中各類別的 @Api 屬性如果有任何差異,都會導致 API 設定變得「模稜兩可」,因而無法在 App Engine 適用的 Cloud Endpoints Frameworks 內運作。

您可以透過下列幾種方式建立明確的多重類別 API:

  • 以手動方式確認單一 API 中所有類別都有完全相同的 @Api 註解屬性。因為這個方式簡明易懂,此處就不詳加說明。
  • 透過 Java 繼承使用註解繼承。在這個繼承中,單一 API 中的所有類別都會從一般以 @Api 註解的基本類別中,繼承相同的 API 設定。
  • 透過 @ApiReference 註解,將註解繼承用於單一 API 中的所有類別,以參考一般 @Api 註解類別中相同的 API 設定。

針對可能依類別而異的屬性使用 @ApiClass

如要使用這個功能,您必須匯入以下內容:

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

雖然 @Api 註解中的所有屬性都必須與 API 內的所有類別相符,但您還可以透過 @ApiClass 註解,提供在各類別間不需要完全相同的屬性。例如:

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

其中 TicTacToeA 會使用用戶端 ID 許可清單 (其中包含允許的用戶端 ID) 來限制存取權,而 TicTacToeB 則不會限制存取權。

@ApiClass 註解提供的所有屬性在 @Api 註解中都擁有相等的屬性。請注意,@Api 相等屬性預設為整個 API 適用。如果在 @Api 中指定的同一屬性有整個 API 的預設值,則類別特定的 @ApiClass 屬性會覆寫整個 API 的預設值。

下列範例說明類別特定 @ApiClass 相等值覆寫 @Api 屬性的情況:

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

註解繼承

@Api@ApiClass 註解屬性可以從其他類別繼承,個別屬性則能透過 Java 繼承@ApiReference 繼承加以覆寫

使用 Java 繼承

類別如果擴展其他含 @Api@ApiClass 註解的類別,其行為就像使用相同屬性進行註解一樣。例如:

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

註解只會透過 Java 子類別繼承,而不是透過介面實作。例如:

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

因此,系統不支援任何類型的架構註解多重繼承

繼承也適用於 @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 { … }

其中 TicTacToeBoards 會從 BoardsBase 繼承 resource 屬性值 boards,進而覆寫其 @Api 註解中的 resource 屬性設定 (scores)。請記得,如有任何類別在 @Api 註解中指定資源屬性,則所有類別都必須在 @Api 註解中指定相同設定;此繼承技巧可讓您覆寫這個 @Api 屬性。

使用 @ApiReference 繼承

如要使用這個功能,您必須匯入以下內容:

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

@ApiReference 註解提供了另一種指定註解繼承的方式。類別如果藉由 @ApiReference 來指定其他含 @Api@ApiClass 註解的類別,其行為就像使用相同屬性進行註解一樣。例如:

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

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

如果同時使用 Java 繼承和 @ApiReference,註解只會透過 @ApiReference 註解繼承。類別上透過 Java 繼承所繼承的 @Api@ApiClass 註解都會遭到忽略。例如:

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

覆寫已繼承的設定

無論您是使用 Java 繼承還是 @ApiReference 來繼承設定,都能使用新的 @Api@ApiClass 註解覆寫繼承的設定。系統只會覆寫新註解中指定的設定屬性,未指定的屬性仍保留繼承值。例如:

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

覆寫繼承也適用於 @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 { … }

在透過 @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 註解

@ApiMethod 註解可以從已複寫的方法繼承。例如:

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

@Api@ApiClass 註解繼承相似,如果互相覆寫的多個方法都有 @ApiMethod 註解,則可以覆寫個別屬性。例如:

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

由於沒有 @ApiReference 註解或相等的方法,因此一律會透過 Java 繼承來繼承 @ApiMethod,而非透過 @ApiReference

繼承和優先順序規則

下表概要列出前面討論的繼承規則和優先順序。

註解/繼承 規則
@Api 所有類別必須相同。
@ApiClass 為類別指定以覆寫 @Api 屬性。
Java 繼承 類別繼承基本類別的 @Api@ApiClass
@ApiReference 類別繼承參考類別的 @Api@ApiClass
在從基本類別繼承的類別 (Java) 上使用 @ApiReference 類別繼承參考類別的 @Api@ApiClass,而「不是」從基本類別繼承。

註解繼承的常見用途

以下為繼承的常見用途範例:

用於 API 版本管理:

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

用於多重類別 API:

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

用於測試相同 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;
  }
}

其中 someMethod 可能會傳回預先建立的回應、避免具副作用的呼叫、略過網路或資料儲存庫要求等等。

新增類別到 web.xml

將類別加註之後,您必須將類別新增到 web.xml 檔案中。以下範例說明單一類別:

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

如何新增多個類別:

  1. <param-value>com.example.skeleton. MyApi</param-value> 替換為您自己的 API 類別名稱。

  2. 在以逗號分隔的相同 <param-value> 欄位中新增每個類別,如下所示:

    <param-value>com.example-company.example-api.Hello,com.example-company.example-api.Goodbye</param-value>
    
本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
App Engine 適用的 Cloud Endpoints Frameworks
需要協助嗎?請前往我們的支援網頁