LoginSignup
1
0

More than 5 years have passed since last update.

GAE/Java8試行(その8:「Cloud Endpoints Frameworks」)

Last updated at Posted at 2018-11-05

お題

WebAPIを楽に開発しようと思った時、素のサーブレットを使うのはしんどいので、Cloud Endpoints Frameworksを導入する。
楽なポイントとしては、↓

  • Mavenコマンド一発でスケルトン(やチュートリアル)プロジェクトの生成が可能
  • アノテーションベースでAPI定義が可能
  • APIメソッドの引数をオブジェクトで受け取るようにすると勝手にJSON構造をマッピングしてくれる
  • APIキーによるアクセス制限がアノテーションの属性一発で可能
  • JSON Web TokenやFirebase Authenticationといったユーザー認証の仕組みもアノテーションベース

GAE試行Index

開発環境

# OS

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="17.10 (Artful Aardvark)"

# Java

$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

# IDE

みんな大好きIntelliJ IDEA

実践

ソースの全量は↓
https://github.com/sky0621/java-webapi-for-gae-study/tree/d67ad8d3ea5816f26c5bfad3bb72215963042f60/com.example.sky0621.fs

■Mavenプロジェクト生成

今回は、一からCloud Endpoints Frameworksを試すので最小のスケルトンでMavenプロジェクトを生成。
(groupId, artifactId, packageすべて「com.example.sky0621.fs」を入力)

$ mvn archetype:generate -Dappengine-version=1.9.67 -Djava8=true -Dfilter=com.google.appengine.archetypes:
[INFO] Scanning for projects...
     ・・・
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: remote -> com.google.appengine.archetypes:appengine-flexible-archetype (A basic Java application with Google App Engine flexible.)
2: remote -> com.google.appengine.archetypes:appengine-skeleton-archetype (A skeleton application with Google App Engine)
3: remote -> com.google.appengine.archetypes:appengine-standard-archetype (A basic Java application with Google App Engine Standard)
4: remote -> com.google.appengine.archetypes:endpoints-skeleton-archetype (A skeleton project using Cloud Endpoints Frameworks with Google App Engine Standard)
5: remote -> com.google.appengine.archetypes:guestbook-archetype (A guestbook application with Google App Engine)
6: remote -> com.google.appengine.archetypes:hello-endpoints-archetype (A simple starter application using Cloud Endpoints Frameworks with Google App Engine Standard)
7: remote -> com.google.appengine.archetypes:skeleton-archetype (Archetype with a README about Google App Engine archetypes)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 4

すると、こんな感じでプロジェクトが出来上がる。

$ tree
.
├── LICENSE
├── README.md
├── build.gradle
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── sky0621
        │               └── fs
        │                   └── YourFirstAPI.java
        └── webapp
            └── WEB-INF
                ├── appengine-web.xml
                ├── logging.properties
                └── web.xml

唯一生成されたJavaクラス(YourFirstAPI.java)の中身は「@Apiアノテーション」とクラス定義のみ。

@Api(name = "skeleton-api",
     version = "v1")
public class YourFirstAPI {
}

■CRUD-API作成

設計

というほどの大層なものではなく、単にAPIとして扱うリソースを決め、機能を定義する。

リソース

書籍 - book

論理名 物理名
ASIN asin string
書籍名 bookName string
著者名 authors string[]
価格 price int

機能

機能名 HTTPメソッド URL リクエスト レスポンス
登録 POST {hostname}/_ah/api/fs/v1/books bookオブジェクト none
参照 GET {hostname}/_ah/api/fs/v1/books/{asin} none bookオブジェクト
一覧 GET {hostname}/_ah/api/fs/v1/books none bookオブジェクトリスト
更新 PUT {hostname}/_ah/api/fs/v1/books/{asin} bookオブジェクト none
削除 DELETE {hostname}/_ah/api/fs/v1/books/{asin} none none

実装

登録時や更新時のリクエストパラメータ並びに一覧取得時のレスポンスに用いる構造。

public class Book {
    private String asin;
    private String bookName;
    private List<String> authors;
    private int price;

    public Book() {
    }

    public Book(String asin, String bookName, List<String> authors, int price) {
        this.asin = asin;
        this.bookName = bookName;
        this.authors = authors;
        this.price = price;
    }

    public String getAsin() {
        return asin;
    }

    public void setAsin(String asin) {
        this.asin = asin;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public List<String> getAuthors() {
        return authors;
    }

    public void setAuthors(List<String> authors) {
        this.authors = authors;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

}

あと、とりあえず永続化はメモリにしておくので、そのための器を適当に作っておく。

public class BookSaver {
    private static List<Book> bookList = new ArrayList<>();

    public static void add(Book book) {
        bookList.add(book);
    }

    public static Book get(String asin) {
        for (Book b : bookList) {
            if (b.getAsin().equals(asin)) {
                return b;
            }
        }
        return null;
    }

    public static List<Book> list() {
        return bookList;
    }

    public static void update(Book book) {
        List<Book> bl = new ArrayList<>();
        for (Book b : bookList) {
            if (b.getAsin().equals(book.getAsin())) {
                bl.add(book);
            } else {
                bl.add(b);
            }
        }
        bookList = bl;
    }

    public static void delete(String asin) {
        List<Book> bl = new ArrayList<>();
        for (Book b : bookList) {
            if (!b.getAsin().equals(asin)) {
                bl.add(b);
            }
        }
        bookList = bl;
    }
}

最後に、APIメソッドを定義。

@Api(name = "fs",
     version = "v1")
public class YourFirstAPI {
    @ApiMethod(name = "books", path = "books", httpMethod = ApiMethod.HttpMethod.POST)
    public void addBook(Book book) {
        BookSaver.add(book);
    }

    @ApiMethod(name = "books", path = "books/{asin}", httpMethod = ApiMethod.HttpMethod.GET)
    public Book getBook(@Named("asin") String asin) {
        return BookSaver.get(asin);
    }

    @ApiMethod(name = "books", path = "books", httpMethod = ApiMethod.HttpMethod.GET)
    public List<Book> listBooks() {
        return BookSaver.list();
    }

    @ApiMethod(name = "books", path = "books/{asin}", httpMethod = ApiMethod.HttpMethod.PUT)
    public void updateBook(@Named("asin") String asin, Book book) {
        BookSaver.update(book);
    }

    @ApiMethod(name = "books", path = "books/{asin}", httpMethod = ApiMethod.HttpMethod.DELETE)
    public void deleteBook(@Named("asin") String asin) {
        BookSaver.delete(asin);
    }
}

これだけ。

動作確認

まずビルド。

$ mvn clean package

続いて

$ mvn endpoints-framework:openApiDocs

さらに、ローカルでWebAPIサーバ起動。

$ mvn appengine:run

試しに、↓の2つを「http://localhost:8080/_ah/api/fs/v1/books」にPOSTする。

{
    "asin": "4873117585",
    "bookName": "ゼロから作るDeep Learning",
    "authors": ["斎藤 康毅"],
    "price": 3672
}
{
    "asin": "B07J6FP6NQ",
    "bookName": "Vue.js入門",
    "authors": ["川口 和也", "喜多 啓介", "野田 陽平", "手島 拓也", "片山 真也"],
    "price": 3650
}

http://localhost:8080/_ah/api/fs/v1/books」にGETすると、↓のような結果が返る。

{
    "items": [
        {
            "asin": "4873117585",
            "bookName": "ゼロから作るDeep Learning",
            "authors": [
                "斎藤 康毅"
            ],
            "price": 3672
        },
        {
            "asin": "B07J6FP6NQ",
            "bookName": "Vue.js入門",
            "authors": [
                "川口 和也",
                "喜多 啓介",
                "野田 陽平",
                "手島 拓也",
                "片山 真也"
            ],
            "price": 3650
        }
    ]
}

http://localhost:8080/_ah/api/fs/v1/books/4873117585」にGETすると、指定の1件が返る。

{
    "asin": "4873117585",
    "bookName": "ゼロから作るDeep Learning",
    "authors": [
        "斎藤 康毅"
    ],
    "price": 3672
}

http://localhost:8080/_ah/api/fs/v1/books/4873117585」にDELETEすると、指定の1件が削除される。

再度「http://localhost:8080/_ah/api/fs/v1/books」にGETすると、DELETEした書籍以外の登録情報が返る。

{
    "items": [
        {
            "asin": "B07J6FP6NQ",
            "bookName": "Vue.js入門",
            "authors": [
                "川口 和也",
                "喜多 啓介",
                "野田 陽平",
                "手島 拓也",
                "片山 真也"
            ],
            "price": 3650
        }
    ]
}

更新は省略。

参考

まとめ

今回のは、あくまでCloud Endpoints Frameworksのさわりでしかないけど、WebAPI作るにはデフォルトで用いてもいいくらい楽。
今後、APIキーや認証といった、より便利さが実感できる機能も試してみよう。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0