Web APIの設計
Web APIの設計でExcelを使って定義書を作成していたが、仕様の変更等によって気がついたら設計書とシステムに乖離しているなんてことがあった。そのようなことがないようにWeb APIの定義情報を常に正とするようなアプリにしたい。
そこでSwaggerは、いくつかの企業によるコンソーシアムによって、Web APIの標準化を行うための規約とそのツール群を使う。
Swagger
Swaggerのサイトに行くとSwagger-editorやSwagger-UIなどツールがいくつかあるけど、swagger-nodeとbootprint-openapiで一通りできる。
GitHub - swagger-api/swagger-node: Swagger module for node.js
$ npm install -g swagger
GitHub - bootprint/bootprint-openapi: Bootprint-module to render OpenAPI specifications
$ npm install -g bootprint $ npm install -g bootprint-openapi $ npm -g install html-inline
手順としては、以下。
Swagger-editorでWeb API定義ファイルを作成する
はじめにプロジェクトのディレクトリを作る。
その際にNodeJSのwebフレームワークを選択させられる。これは後で作るモックサーバをカスタマイズするときに改造していくものである(Flaskが対応していたら便利だけど)。
express
を選択する。
$ swagger project create example
次に生成されたディレクトリに降りて、エディタを起動する。
$ cd example/ $ swagger project edit
Webブラウザが起動し、エディタが表示される。
GUIに対応できない環境(x windowを起動していないサーバなど)で利用するときは、-s | --silent
を付ける。
今回はSwagger Editorのpetstores_simple.yamlを使う。
ドキュメント化する
bootprint-openapiでSwaggerファイルをHTML化する。
$ pwd example $ bootprint openapi api/swagger/swagger.yaml target
するとtargetディレクトリ配下にhtml, css, javascriptが書き出され、適当なwebサーバに配置することで閲覧できる。 さらにcss, javascriptをHTMLにまとめたいときは下記のようにし、dist.htmlを公開すれば良い。
$ html-inline target/index.html > target/dist.html
モックサーバを立てる
モックサーバはサーバ機能をまだ実装していない段階で、クライアント側の仕様検討、実装を待ちにすることなく行う目的で建てる。
$ pwd example $ swagger project start -m
クライアント側(同一端末だが)でAPIを叩く。
(2016.12.12 修正: 下記で/petsを叩いていたのだけど、そもそもbathPath: /api
なので、
そのようなAPIはないのは当たり前だった。
$ curl -i http://localhost:10010/api/pets HTTP/1.1 404 Not Found X-Powered-By: Express X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Content-Length: 21 Date: Mon, 12 Dec 2016 04:50:26 GMT Connection: keep-alive Cannot GET /api/pets
おや? 同じ事象がRunning in mock modeのチュートリアル例でも起きる。
調べてみると、下記のissueに遭遇。x-swagger-router-controller
を指定しないとダメとのこと。
これを付けるのは、pathかoperationの階層で定義すれば良い。これとoperationId
でAPIから呼ばれるメソッドを指定する。
issueでも言われているけど、モックなのにこれを指定しないといけないのは使い勝手が悪い気が...。
Flaskでswaggerに沿ったサーバ機能を作ることができるconnexionでは、むしろoperationレベルでのみでxx-swagger-router-controller
を設定できる。これだとoperationId
のみでもう良くないかと思えてきた。なのでモック使うとき以外は特に使用しないことにする。
pathに含まれるoperationで全て同じとする。
..(省略).. /pets: x-swagger-router-controller: pets ..(省略).. /pets/{id}: x-swagger-router-controller: pets ..(省略)..
改めてクライアント側で叩く。 そのまま使うと決められた値(numberなら1, stringなら"Sample text")が返ってくる。 レスポンスをカスタマイズしたい場合は、Implementing mock mode controllersを参照。
$ curl -X GET -i http://localhost:10010/api/pets HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:08:50 GMT Connection: keep-alive Content-Length: 51 [{"id":1,"name":"Sample text","tag":"Sample text"}] $ cat pet.json {"id":1,"name":"foo","tag":"dog"} $ curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" -i http://localhost:10010/api/pets -d @pet.json HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:29:25 GMT Connection: keep-alive Content-Length: 49 {"id":1,"name":"Sample text","tag":"Sample text"} $ curl -X GET -i http://localhost:10010/api/pets/1 HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:36:02 GMT Connection: keep-alive Content-Length: 49 {"id":1,"name":"Sample text","tag":"Sample text"} $ curl -X DELETE -i http://localhost:10010/api/pets/1 HTTP/1.1 500 Internal Server Error X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:37:25 GMT Connection: keep-alive Content-Length: 196 {"message":"Response validation failed: void does not allow a value","code":"INVALID_TYPE","failedValidation":true,"path":["paths","/pets/{id}","delete","responses","204"],"originalResponse":"{}"}
最後だけこけた。どうやらレスポンスが空は許可されていないみたい。 対象療法だけど、レスポンスを追加した。
delete: description: deletes a single pet based on the ID supplied operationId: deletePet parameters: - name: id in: path description: ID of pet to delete required: true type: integer format: int64 responses: '200': description: pet deleted schema: $ref: '#/definitions/pet' default: description: unexpected error schema: $ref: '#/definitions/errorModel'
どうにか。
$ curl -X DELETE -i http://localhost:10010/api/pets/1 HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json Date: Sat, 24 Sep 2016 13:51:11 GMT Connection: keep-alive Content-Length: 49 {"id":1,"name":"Sample text","tag":"Sample text"}
サーバ機能の雛形を生成する
最後にAPI仕様書からサーバ機能の雛形を作る
swagger-nodeの方ではないSwagger-Editorを用いると、サーバ機能のダウンロードができる(swagger-codegenでも良い)。 インストールしても良いが、Dockerイメージがあるので、コンテナにて行う。
$ docker pull swaggerapi/swagger-editor $ docker run -p 80:8080 swaggerapi/swagger-editor
あとはhttp://(YOUR_CONTAINER_URI)にアクセスし、API仕様書の内容をコピー&ペーストして、[Generate Server]からお好みのフレームワークの雛形をダウンロードする。これからFlaskでサーバ機能を作るので、Flaskを選択する。