swagger
spring-boot
Springfox
swagger-codegen
RestTemplate

SpringFoxで吐き出したSwaggerのjsonを使ってswagger-codegenでAPIのclientを吐き出す

この資料について

  • swagger-codegenを使ってRestTemplateを使ったClientを吐き出すやり方をまとめた
  • SpringFoxの内容と合わせて見てほしい
  • あくまで自分が使いそうって思ったものしかまとめてないです

swagger-codegenについて

  • 公式はこちら Swagger Codegen | API Development Tools | Swagger
  • スタブやClientを吐き出してくれるジェネレーター
  • いろんな言語やライブラリに対応していて、RestTemplate以外にもretrofitとかにも対応してるし、そもそもjava以外にもswiftとかnodeとか結構いろいろ対応している
  • コードは一つのプロジェクトとして吐き出されて、jarにまとめてmavenリポジトリとかに上げてdependencyに追加して利用するとかできる
  • 出力されたコードはライセンスとか気にせず自分が作ったものとして利用できる

とりあえず使ってみる

swaggerのjsonを準備する

  • 今回はSpringFoxで出力されたjsonを利用する。そのためアプリケーションを起動してjsonを読み取れるようにすればOK。一応json置いとく(人が読み取れるものではないのでフォーマットとか無視)
  • localでSpringFoxに対応したアプリケーションを立ち上げている場合は http://localhost:8080/v2/api-docs をアクセスすれば見れる
{"swagger":"2.0","info":{"description":"Api Documentation","version":"1.0","title":"Api Documentation","termsOfService":"urn:tos","contact":{},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0"}},"host":"localhost:8080","basePath":"/","tags":[{"name":"families","description":"families info"}],"schemes":["http","https"],"paths":{"/families":{"post":{"tags":["families"],"summary":"create","operationId":"createUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"familyRequest","description":"familyRequest","required":true,"schema":{"$ref":"#/definitions/FamilyRequest"}}],"responses":{"200":{"description":"OK","schema":{"type":"object"}},"400":{"description":"Parameter is invalid"},"401":{"description":"認証エラー"},"500":{"description":"Internel server error"}},"deprecated":false}},"/families/list":{"get":{"tags":["families"],"summary":"list","operationId":"listUsingGET","produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/FamiliesListResponse"}},"400":{"description":"Parameter is invalid"},"500":{"description":"Internel server error"}},"deprecated":false}},"/families/{familyId}":{"get":{"tags":["families"],"summary":"findFamily","operationId":"findFamilyUsingGET","produces":["*/*"],"parameters":[{"name":"familyId","in":"path","description":"familyId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Family"}},"400":{"description":"Parameter is invalid"},"500":{"description":"Internel server error"}},"deprecated":false}},"/families/{familyId}/members/list":{"get":{"tags":["families"],"summary":"findMembersByFamilyId","operationId":"findMembersByFamilyIdUsingGET","produces":["*/*"],"parameters":[{"name":"familyId","in":"path","description":"familyId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/MembersResponse"}},"400":{"description":"Parameter is invalid"},"500":{"description":"Internel server error"}},"deprecated":false}}},"definitions":{"FamiliesListResponse":{"type":"object","properties":{"families":{"type":"array","items":{"$ref":"#/definitions/Family"}}},"title":"FamiliesListResponse"},"Family":{"type":"object","properties":{"familyId":{"type":"integer","format":"int32"},"familyName":{"type":"string"},"updatetime":{"type":"string","format":"date-time"}},"title":"Family"},"FamilyRequest":{"type":"object","properties":{"familyId":{"type":"integer","format":"int32"},"familyName":{"type":"string"},"members":{"type":"array","items":{"$ref":"#/definitions/MemberRequest"}},"updatetime":{"type":"string","format":"date-time"}},"title":"FamilyRequest"},"Member":{"type":"object","properties":{"birthday":{"type":"string","format":"date"},"gender":{"type":"string","enum":["MALE","FEMALE","ETC","NONE"]},"givenName":{"type":"string"},"memberId":{"type":"integer","format":"int32"},"updatetime":{"type":"string","format":"date-time"}},"title":"Member"},"MemberRequest":{"type":"object","properties":{"birthday":{"type":"string","format":"date"},"gender":{"type":"string","enum":["MALE","FEMALE","ETC"]},"givenName":{"type":"string"},"memberId":{"type":"integer","format":"int32"}},"title":"MemberRequest"},"MembersResponse":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/definitions/Member"}}},"title":"MembersResponse"}}}

configファイルをつくる

  • 名前は適当でいいのでconfigファイルをつくる。今回はswagger-codegen-config.jsonとした
{
  "java8" : true,
  "dateLibrary": "java8-localdatetime",
  "apiPackage": "com.example.tryspringfox.client",
  "modelPackage": "com.example.tryspringfox.model",
  "library": "resttemplate",
  "hideGenerationTimestamp": true
}

configの解説

  • 詳しくは下記コマンドを実行して確認すること。今回指定したものだけさらっと書いとく
  • けっこういろいろ指定できる。BigDecimalを文字列として扱う、とかそういう細かいのもある
$ swagger-codegen config-help -l java
設定 設定値 内容
java8 true,false 3rdパーティのライブラリを使うかjava8のものを使うか
dataLibrary joda,legacy,java8-localdate,java8,threetenbp Dateクラスで何使うか指定
apiPackage パッケージのパス(com.exampleとか) 生成されるAPIクラスのパッケージを指定
modelPackage パッケージのパス(com.exampleとか) 生成されるModelクラスのパッケージを指定
library restTemplateとか 生成されるクライアントが依存するライブラリを指定
hideGenerationTimestamp true,false 生成されるコードに生成日を記載するか

オプションを指定して生成してみる

  • 下記のオプションを指定して動かす
$ swagger-codegen generate \
-i http://localhost:8080/v2/api-docs \
-l java \
-c ./swagger-codegen-config.json \
-o ../try-swagger-codegen-client

オプションの解説

オプション 内容
-i swaggerのjsonへのパス
-l 言語の指定。javaとか
-c configファイルへのパス
-o 出力先のパス

生成したものを使う

  • jarをdependencyに登録する(ローカルでやる場合は適当な場所に設置して下記みたいなかんじでやる)
  • 下記のようにDIに登録してあげればあとはよしなに使える
dependencies {
   ...
    compile files("jarのパス")
    ...
}
@Configuration
public class ApiClientConfig {

    @Autowired
    RestTemplateBuilder restTemplateBuilder;

    @Bean
    public FamiliesApi familiesApi(){
        return new FamiliesApi(apiClient());
    }

    @Bean
    public ApiClient apiClient() {
        RestTemplate restTemplate = restTemplateBuilder.setReadTimeout(1000).setConnectTimeout(1000).build();
        ApiClient apiClient = new ApiClient(restTemplate);
        apiClient.setBasePath("http://prod.hostname.com");
        return apiClient;
    }
}

使ってみて雑感

  • ホストがswaggerの仕様だと固定されてしまうが、上記のように変更できる
  • RestTemplateをApiClientクラスをインスタンス化するときに引数として渡せるので、やりたいことはたいてい自前でいろいろ設定できそう
  • クライアントクラスのテストは生成されないが仕様通りにリクエストはできるので、生成されるコードに関してはテストをやらないっていう選択肢はありだと思う
  • 普通にインターセプターとかも使えるので、共通でヘッダを追加したい、とかも問題なく可能
  • jarとしてmavenリポジトリに登録できるので、いろいろなところで同じクライアントクラスをつくる、っていうことにはならなそうなので、マイクロサービス化しているシステムを開発している場合は利用してみても良いと思う

参考にした資料

関連記事

SpringFoxを使ってSwaggerのjsonを吐き出す