SwaggerのPHP実装であるSwagger-PHPの使い方についてLaravelプロジェクトで確認し、Swaggerとはどんなものか調査したメモになります。
Swaggerの全体像については、「RESTful APIの記述標準化を担うSwaggerとは? | NTT Communications Developer Portal」が参考になりました。
Swaggerを利用したアプリケーション開発は、まずSwaggerドキュメントを作成して、サーバーのスタブとクライアントライブラリを生成し、APIロジックとクライアントUIなどを実装していくような流れになるといったところのようです。
Laravelはありませんが、コード生成ツール swagger-codegen には、Laravelの軽量版であるLumen用のサーバースタブは生成可能です。
この記事では、Laravel 5.2プロジェクトでAPIを作成して、Swagger-PHPのアノテーションからSwaggerドキュメントを生成してSwagger-UIでテストするまでを解説します。
つまり、APIの開発とSwaggerドキュメントの開発は平行して行っていく手順を想定しています。
前提条件
この記事内容を試すにあたって必要な準備は、以下のとおりです。
- Laravelの動作するPHPバージョンがインストール済み
- Composerがグローバルにインストール済み
- Laravel 5.2プロジェクトを生成済み
Laravelの実行環境をすぐに作るにはVagrant Homesteadが便利です。
「Laravel homesteadを利用したLaravel 5 ローカル開発環境の構築」も参考にしてください。
Laravel プロジェクトに Swagger-PHP をインストール
Swagger-PHPを、ComposerでLaravelプロジェクトに追加します。
$ composer require zircote/swagger-php
Swagger-PHPプロジェクトのルートディレクトリについて
PHPで、Swaggerドキュメントを生成するには、Swagger-PHPを利用します。
Swagger-PHPは、プロジェクトのソースに記述されたアノテーション(doctrine annotations)を読み込み、Swaggerフィアルを生成するツールです。
Swagger-PHPは、Swaggerとしてのプロジェクトルートはコマンドにて指定しますが、Laravel 5プロジェクトの場合は、app/Http/Controllers
ディレクトリをSwaggerプロジェクトのルートディレクトリとして扱うのが良さそうです。
APIを生成して、アノテーションを記述
Swagger-PHPのアノテーションの記述方法については、Getting startedに解説があります。
ここでは、この解説に沿ってProductモデルとProductのリストを返すAPIを実装し、それぞれにSwagger用のアノテーションを記述してみます。
APIの基本情報
まずは、APIの基本情報を記述します。
アノテーションは、Swaggerプロジェクトのパスに指定したディレクトリ内の任意のPHPファイルであればどこでも追加できます。
appディレクトリをプロジェクトパスとして、app直下にswagger.phpを作成して基本情報のアノテーションを追加してみました。
<?php
// app/swagger.php
/**
* @SWG\Swagger(
* schemes={"http"},
* host="homestead.app",
* basePath"="/api",
* @SWG\Info(
* title="My first swagger documented API",
* version="1.0.0"
* )
* )
*/
モデルにスキーマ定義を記述
Productモデルを作成します。
$ php artisan make:model Product --migration
Model created successfully.
Created Migration: 2016_08_03_091651_create_products_table
マイグレーションの中身は省略します。
Productモデルのコードには、以下のように、アノテーションを追記します。
LaravelのEloquentは、通常フィールドは不要ですが、Swagger用に記述しています。
<?php
// app/Product.ph
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* @SWG\Definition()
*/
class Product extends Model
{
/**
* The product id
* @var integer
*
* @SWG\Property()
*/
public $id;
/**
* The product name
* @var string
*
* @SWG\Property()
*/
public $name;
}
コントローラにAPI定義を記述
次に、コントローラーを作成します。
ここでは、コントローラを名前空間Api
の下に作成し、APIのパスも/api/products
にしてみます。
$ php artisan make:controller Api/ProductController
Controller created successfully.
作成したコントローラに、Productのリストを返す処理を実装して、Swaggerのアノテーションを記述します。
<?php
// app/Http/Controller/Api/ProductController
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Product;
class ProductController extends Controller
{
/**
* @SWG\Get(
* path="/api/products",
* summary="list products",
* @SWG\Response(
* response=200,
* description="A list with products",
* @SWG\Schema(
* type="array",
* @SWG\Items(ref="#/definitions/Product")
* )
* ),
* @SWG\Response(
* response=500,
* description="an "unexpected" error"
* )
* )
*/
public function index()
{
return response()->json([
'result' => [
'statistics' => [
'products' => Product::all()
],
],
'message' => '',
'type' => 'success',
'status' => 0
]);
}
}
@SWG\Responseには、@SWG\Schemaで作成したモデルProductを指定しています。
補足
ところで、Arrays and Objectsの説明では、21行目のresponse
の値は”default”が指定されていますが、AWS API Gatewayでは”default”をサポートしていないので、エラーは500を返すようにしています。
Swagger-UIから動作確認をするために、作成したAPIのルーティングを追加して実際に動くようにしておきます。
<?php
// app/Http/routes.php
Route::get('/api/products', 'Api\ProductController@index');
Swaggerドキュメントを生成する
Swaggerドキュメントは、以下のコマンドを実行すると生成されます。
$ mkdir public/api
$ ./vendor/bin/swagger ./app --output ./public/api/
以下の内容のswagger.jsonが生成されました。
// public/api/swagger.json
{
"swagger": "2.0",
"info": {
"title": "My first swagger documented API",
"version": "1.0.0"
},
"host": "homestead.app",
"basePath": "/api",
"schemes": [
"http"
],
"paths": {
"/products": {
"get": {
"summary": "list products",
"responses": {
"200": {
"description": "A list with products",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Product"
}
}
},
"500": {
"description": "an \"unexpected\" error"
}
}
}
}
},
"definitions": {
"Product": {
"properties": {
"id": {
"description": "The product id",
"type": "integer"
},
"name": {
"description": "The product name",
"type": "string"
}
}
}
}
}
Swagger UIのインストール
swagger-api/swagger-uiをダウンロードして展開し、dist
ディレクトリをLaravelプロジェクトのpublic/swagger
ディレクトリにコピーします。
$ cp -r ~/Downloads/swagger-ui-master/dist public
$ mv public/dist public/swagger
ホスト名homestead.app
の場合、ブラウザでhttp://homestead.app/swagger/index.html
を開き、http://homestead.app/api/swagger.json
を入力して[Explore]をクリックすると、生成されたAPIを表示できます。
[Try it out!]をクリックすると実際にAPIにアクセスし結果を確認できます。
productsテーブルには、幾つかデータを入れた状態でテストしています。
まとめ
Laravel5とSwagger-PHPを組み合わせたAPIドキュメントの生成手順について確認してきましたが、実際にプロジェクトで使うには以下の様なことを考える必要がありそうです。
- APIのバーション管理などを考えると、APIのモデルとEloquentモデルは分けたほうがよさそう
- Swagger-PHPアノテーションの記述量が多いのと、アノテーションのためのコード量が必要になるのがつらい
- LaravelでのAPI実装は、dingo/apiを利用した方が良いので、dingo/apiとSwagger-PHPのプロジェクト構成にしたい
- Swaggervel(補足参照)のような、Laravel5とSwagger-PHPを統合するパッケージの検討
- AWS Gateway APIやAzure API ManagementなどGatewayサービスとの統合
補足: Swaggervel
LaravelプロジェクトにSwagger-PHPとSwagger UIを統合できるパッケージとしては、Swaggervelをはじめ、いくつかパッケージがあるようです。
Swaggervelについても動作を確認してみたので、メモしておきます。
ただし、すんなりは動かなかったのであまりおすすめはできません。
Laravel 5の場合は2.0ブランチを利用する。
slampenny/Swaggervel
$ composer require jlapp/swaggervel:2.0.x-dev
しかし、このブランチではapi-docsでエラーが発生してしまう。
Class ‘Swagger\Swagger’ not found #43
によるとmaster-devだと動くらしい。
$ composer require jlapp/swaggervel:master-dev
Jlapp\Swaggervel\SwaggervelServiceProvider
をapp/config/app.phpのproviders
に追加
<?php
// app/config/app.php
'providers' => [
...
Jlapp\Swaggervel\SwaggervelServiceProvider::class,
],
artisan vendor:publish
コマンドを実行すると、設定ファイルとSwagger-UIのリソースがコピーされます。
$ php artisan vendor:publish
Copied File [/vendor/jlapp/swaggervel/src/config/swaggervel.php] To [/config/swaggervel.php]
Copied Directory [/vendor/jlapp/swaggervel/public] To [/public/vendor/swaggervel]
Copied Directory [/vendor/jlapp/swaggervel/src/views] To [/resources/views/vendor/swaggervel]
Publishing complete for tag []!
Swaggerドキュメントを生成します。
出力先は、storage/docs
ディレクトリを指定します。
$ mkdir storage/docs
$ ./vendor/bin/swagger app/Http/Controllers -o storage/docs/
Swagger-UIのパスは、/api/docs
です(masterはREADMEと異なるので注意)。
Swaggerファイルのパス/doc
がセットされた状態で開きます。
Swaggervelに含まれるSwagger-UITは古そうですね。
Swagger-PHPとSwagger-UI一つのパッケージにまとめているだけなので、ドキュメントの通りに動かないこと、Swagger-UIが古いことなどを考えると、それぞれ個別に管理するようにした方が良いかもしれません。
参考記事
Integrate Swagger into Laravel
LaravelプロジェクトのAPIをswaggerを使ってドキュメント化
Getting started with Swagger and Swaggervel
次にやること
この内容を踏まえ、次にAWS Gateway APIとMicrosoftのAPI ManagementにおけるSwaggerインポートについて調べてみる予定です。