REST API をタイプセーフに呼び出す(Spring Boot + Swagger Code Generator)
はじめに
REST API をタイプセーフに呼び出したいのです。
その実現のため WADLとかを追いかけてたんですが、Swagger は完全にノーマークでした。 WADLは、実質終わっているような。
昨年(2015年11月)、Microsoft, Google, IBM などにより Open API Initiative という団体が結成されました。 Swagger をベースに、REST API の記述標準化を目指しているようです。
で、今回、「Spring Boot で作成した REST API を Swagger Code Generator により自動生成された Java のコードを利用して呼び出す」ということを試してみました。ソースは、github にアップしています。
サンプルコード
Server
まずは、Spring Boot で簡単な REST API を作ります。
Application.java
main メソッドを持つアプリケーションを起動するクラスです。
package server; import static springfox.documentation.builders.PathSelectors.*; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger.web.UiConfiguration; import springfox.documentation.swagger2.annotations.EnableSwagger2; @SpringBootApplication @EnableSwagger2 // (1) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public Docket documentation() { return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any()) .paths(regex("^/(?!error).*$")).build().pathMapping("/").apiInfo(metadata()); } @Bean public UiConfiguration uiConfig() { return UiConfiguration.DEFAULT; } private ApiInfo metadata() { return new ApiInfoBuilder().title("Calculate API").version("1.0").build(); } }
(1)で EnableSwagger2 アノテーションを付けています。これを付けるだけで、指定のURLにアクセスすることにより、Open API Spec が取得できるようになります。
CalculateController.java
単純な Controller クラスを作成します。二つの整数をリクエストで受け取り、足し算・引き算の結果をレスポンスするだけです。
package server; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class CalculateController { @RequestMapping(value = "/calculate", method = RequestMethod.GET) public Result get(@RequestParam Integer arg1, @RequestParam Integer arg2) { Result result = new Result(); result.add = arg1 + arg2; result.subtract = arg1 - arg2; return result; } public static class Result { public Integer add; public Integer subtract; } }
(自分の環境だとClass 'springfox.documentation.swagger.web.ClassOrApiAnnotationResourceGrouping' is marked deprecated
のような警告が消せない。。)
アプリケーションを起動し、http://localhost:8080/calculate?arg1=2&arg2=1
とアクセスすると、{"add":3,"subtract":1}
の JSON がレスポンスされます。
http://localhost:8080/v2/api-docs
にアクセスすると Open API Spec が確認できます。以下のように表示されます(見やすいようにフォーマットしてます)。
{ "basePath": "/", "definitions": { "Result": { "properties": { "add": { "format": "int32", "type": "integer" }, "subtract": { "format": "int32", "type": "integer" } } } }, "host": "localhost:8080", "info": { "contact": {}, "license": {}, "title": "Calculate API", "version": "1.0" }, "paths": { "/calculate": { "get": { "consumes": ["application/json"], "operationId": "getUsingGET", "parameters": [ { "description": "arg1", "format": "int32", "in": "query", "name": "arg1", "required": true, "type": "integer" }, { "description": "arg2", "format": "int32", "in": "query", "name": "arg2", "required": true, "type": "integer" } ], "produces": ["*/*"], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/Result" } }, "401": { "description": "Unauthorized" }, "403": { "description": "Forbidden" }, "404": { "description": "Not Found" } }, "summary": "get", "tags": ["calculate-controller"] } } }, "swagger": "2.0", "tags": [ { "description": "Calculate Controller", "name": "calculate-controller" } ] }
Client のコードを生成する
Swagger Code Generator の jar を取得し、Java Client のコードを生成します。
$ wget http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.1.5/swagger-codegen-cli-2.1.5.jar $ java -jar swagger-codegen-cli-2.1.5.jar generate \ -i http://localhost:8080/v2/api-docs \ -l java \ -o out/java
生成されたコードは、JAX-RS や Joda に依存していてちょっと残念な感じではあります。
swagger codegen は、複数の言語に対応しており、以下ように確認できます。
$ java -jar swagger-codegen-cli-2.1.5.jar langs Available languages: [android, async-scala, csharp, dart, flash, python-flask, java, javascript, jaxrs, inflector, jmeter, nodejs, objc, perl, php, python, qt5cpp, ruby, scala, scalatra, silex-PHP, sinatra, slim, spring-mvc, dynamic-html, html, swagger, swagger-yaml, swift, tizen, typescript-angular, typescript-node, akka-scala, CsharpDotNet2, clojure]
typescript-angular じゃなく typescript-jquery が欲しい感じ。 Codegen のカスタマイズについては、この辺の記事が参考になりそう。
依存ライブラリを選ぶ(2016-01-24追記)
依存ライブラリを選べるみたいです。
javaの場合の確認コマンド
java -jar swagger-codegen-cli-2.1.5.jar config-help -l java
出力
(省略) library library template (sub-template) to use (Default: <default>) <default> - HTTP client: Jersey client 1.18. JSON processing: Jackson 2.4.2 feign - HTTP client: Netflix Feign 8.1.1 jersey2 - HTTP client: Jersey client 2.6 okhttp-gson - HTTP client: OkHttp 2.4.0. JSON processing: Gson 2.3.1 retrofit - HTTP client: OkHttp 2.4.0. JSON processing: Gson 2.3.1 (Retrofit 1.9.0) retrofit2 - HTTP client: OkHttp 2.5.0. JSON processing: Gson 2.4 (Retrofit 2.0.0-beta2)
feign に依存させる場合
$ java -jar swagger-codegen-cli-2.1.5.jar generate \ -i http://localhost:8080/v2/api-docs \ -l java -Dlibrary=feign \ -o out/java
Client のコードを利用する
Client のコードを利用するテストコードを作成します。
package client; import static org.junit.Assert.*; import org.junit.Test; import io.swagger.client.ApiClient; import io.swagger.client.ApiException; import io.swagger.client.api.CalculatecontrollerApi; import io.swagger.client.model.Result; public class CalculatecontrollerApiTest { @Test public void testGetUsingGET() throws ApiException { ApiClient client = new ApiClient(); client.setBasePath("http://localhost:8080"); CalculatecontrollerApi api = new CalculatecontrollerApi(client); Result result = api.getUsingGET(2, 1); // (1) System.out.println(result.getAdd()); System.out.println(result.getSubtract()); assertEquals(new Integer(3), result.getAdd()); assertEquals(new Integer(1), result.getSubtract()); } }
(1)でサーバにリクエストしています。このテストを実行すると、標準出力に以下のように出力されます。
3 1
こんな感じで、REST API がタイプセーフに呼び出せるようになるわけです!
終わりに
アプリケーション間の通信と言えば、CORBA や SOAP がありましたが、あまり流行りませんでしたね。今はさらに Thrift とか gRPC とか色々ありますね。JVM言語縛りだったら RMI で良いんじゃないかとも思っていますが。
一番受け受け入れられたのは、ボヤっとした通信をしている REST API でした。そこに Swagger は、ドキュメントの自動生成やクライアントのコードの自動生成をもたらしました(WADLは何故流行らなかったんだろう)。緩く開発したい派は、自動生成されたドキュメントをみて自分でクライアントのコード書いて開発すればいいし、厳格に開発したい派は、自動生成されたクライアントコードを利用すればいいし、選択の余地があって良いのかなーと思うところです。