Java クライント開発における Web API の実装アプローチ:その2 一般的なREST API編
それでは、今回から実際にそれぞれのWeb APIリクエストの実装アプローチを見ていきたいと思います。
最初はベーシックかつ、ふんわりとした取り決めである REST API から。
最初の記事はこちらから。
RESTについておさらい
ここではあまり多く語りませんが、REST はプロトコルや規約ではなく、あくまで Web API の設計思想、ソフトウェアアーキテクチャの一つであるという点です。
REST = Representational State Transfer
Representational State Transfer (REST) は、ウェブのような分散ハイパーメディアシステムのためのソフトウェアアーキテクチャのスタイルのひとつである。
引用元:Wikipedia REST
以下の4つの原則に従った Web API アーキテクチャをREST ful と呼んだりしますね。
Stateless:ステートレスなクライアント/サーバプロトコル Uniform Interface:すべての情報(リソース)に適用できるHTTPメソッドの定義 Addressability:リソースを一意に識別する「汎用的な構文(URL)」の定義 Connectability:アプリケーションの情報と状態遷移の両方を扱うことができる「ハイパーメディア(リソースリンク)の使用」
ただ、今回の記事では REST の原則には従っているが、それ以外の仕様に準拠しているわけではない、普遍的かつドキュメント以上のものが提供されていない Web API のことを示すこととしています。(これは別に REST を卑下しているわけではなく、今後のAPIスタイルとの比較のためです)
対象の REST API と前提条件
対象とする Web API は CData API Server を使って開発したものを利用します。
ODataプロトコルベースで、Swagger(OpenAPI)ドキュメントを公開していますが、特に今回は OData や Swagger であることを意識しません。
あくまでドキュメント以上のものは無い(SDKなども含めて)、という前提に立ちます。
ですので、シンプルに Java のオープンソースライブラリを使ってアクセスします。
APIアクセス用のトークンは、要望があれば公開する予定です。
操作はシンプルに注文データ(orders)と注文明細データ(order_details)を取得して結合したものをコンソールで出力するというものです。
利用ライブラリ
Java から API へのアクセスについては「OkHttpClient」と「GSON」、2つのライブラリを使用しました。
・OkHttpClient
・GSON
実装コード
以下のサイトで公開しています。
package com.company; import com.google.gson.Gson; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import java.util.List; import java.util.stream.Collector; import java.util.stream.Collectors; public class Main { public static final String url = "http://cdatanorthwindsampleapiserver.azurewebsites.net/api.rsc/"; public static final String categories = "categories"; public static final String orders = "orders"; public static final String order_details = "order_details"; public static final String products = "products"; public static final String authHeaderName = "x-cdata-authtoken"; public static final String authHeaderValue = "XXXXXXXXXXX"; public static void main(String[] args) throws Exception { OkHttpClient client = new OkHttpClient(); Response ordersResponse = client.newCall( new Request.Builder().addHeader(authHeaderName,authHeaderValue).url(url + orders).build() ).execute(); Response orderDetailsResponse = client.newCall( new Request.Builder().addHeader(authHeaderName,authHeaderValue).url(url + order_details).build() ).execute(); Gson gson = new Gson(); OrderRootObject orders = gson.fromJson(ordersResponse.body().string(), OrderRootObject.class); OrderDetailRootObject orderDetails = gson.fromJson(orderDetailsResponse.body().string(), OrderDetailRootObject.class); for (OrderDetail orderDetail : orderDetails.Value) { List<Order> order = orders.Value.stream().filter(x -> x.OrderId.equals(orderDetail.OrderId)).collect(Collectors.toList()); if(order.isEmpty()) continue; System.out.println("--------------------------------------------"); System.out.println("OrderId : " + order.get(0).OrderId); System.out.println("CustomerId : " + order.get(0).CustomerId); System.out.println("EmployeeId : " + order.get(0).EmployeeId); System.out.println("Discount : " + orderDetail.Discount); System.out.println("ProductId : " + orderDetail.ProductId); System.out.println("Quantity : " + orderDetail.Quantity); System.out.println("UnitPrice : " + orderDetail.UnitPrice); } } }
Java クライアントアプリケーションから REST API を利用するにあたってのポイント
実装は至ってシンプルにOkHttpClient を使って、Getリクエストしています。
OkHttpClient client = new OkHttpClient(); Response ordersResponse = client.newCall( new Request.Builder().addHeader(authHeaderName,authHeaderValue).url(url + orders).build() ).execute();
そして、JSONを使って、対象クラスへデシリアライズしますが、
Gson gson = new Gson(); OrderRootObject orders = gson.fromJson(ordersResponse.body().string(), OrderRootObject.class);
ちょっと面倒なのがデシリアライズ先のクラス作成です。
このくらいのカラム数ならそこまで面倒ではありませんが、ドキュメントから一つ一つ型などをチェックして、作成しなければいけません。
package com.company; import com.google.gson.annotations.SerializedName; public class Order { @SerializedName("order_id") public String OrderId; @SerializedName("customer_id") public String CustomerId; @SerializedName("employee_id") public String EmployeeId; }
JSONをベースにクラスを生成するツールなどもありますが、どこまでのJSONを対応させるのか? 動的にJSONのカラムが変わる場合などの考慮を考えると、面倒なシチュエーションも多いです。
もちろん、シンプルな REST API であれば、これで十分かなとは思いますが、これに加えてビジネスロジックも記述していくとなると、モデル部分としてはやや脆弱な部分があるのは否めないかなと思います。
また、今回は単純なリソース指定のみのリクエストでしたが、URLパラメータをどうするか、ページング処理などもどのように実装するかはドキュメントから一つ一つ読み取って実装していかないといけないのが、なかなか大変なところかと思います。
おわりに
単純に REST API を実装するだけでも、ドキュメントだけだとなかなか心許ない部分が見えてくるかなと思います。
次回はそんな部分を踏まえながら、OpenAPI(Swagger) ベースの実装を紹介します。