前文
クライアント/サーバールールを記述および文書化するため
Rest-apiを使用した相互作用3つの主要な方法を区別できます。
- 指でサーバーにアクセスするためのルールを同僚に説明する
この方法は迅速であり、長期的なサポートを必要としませんが、あなたはそれに負けてしまう可能性が高いです。
- 手でプロジェクトのGoogleドキュメント/ Wiki / Readmeを作成する
便利なことに、一度書かれたドキュメントは、2番目の説明を必要としません。 同僚や、時には顧客にも見せることができます。 この方法の欠点は、そのようなドキュメントの長期的なサポートです。 プロジェクトのApiが、思考自体が「そしていつドキュメントを更新したのか」というようなサイズに成長したとき。 背中に寒気を引き起こします、そしてあなたはそれがこのように続くことができないことを理解します。 正式には、小さな修正でドキュメントを頻繁に更新できますが、これは最初の休暇前です。 自動文書化システムを使用する
そして、最初の2つの方法の欠点を解決するために、人類は自動文書化システムを思いつきました。 主なアイデアは、特定のプラグインをプロジェクトに添付し、コードに関する情報を収集し、ドキュメント自体を作成し、読み取り可能な形式でラップすることです。 しかし、この方法を使用するほとんどのソリューションは完全ではありません。 最小限の動きでプロジェクトのドキュメントを取得するのに役立つツールを作成してみましょう
問題
コードの自己文書化を確保するために、注釈が主に使用されます。つまり、特定の情報を伝えるコードのコメントセクションです。 しかし、このアプローチにはいくつかの重大な欠点もあります。
すでに記述されているプロジェクトに注釈を追加します。
すでに実装されているプロジェクトのすべてのコントローラーのすべてのメソッドに注釈を追加するのは、かなり日常的な作業です。 大量のコードを使用すると、このような単純なタスクでもミスを犯しやすくなります。
注釈のサポート。
実際、注釈はこの問題を解決するものではなく、コードサポートと組み合わせるだけです。 そのため、プロジェクト全体に散らばっているすべてのコメントの関連性を追跡する必要があります。
コードの乱雑さ
実証するために、Swaggerシステムの古典的な例を見て、 コントローラーがどのように機能するかを見てみましょう
/** * @SWG\Get( * path="/pet/{petId}", * summary="Find pet by ID", * description="Returns a single pet", * operationId="getPetById", * tags={"pet"}, * consumes={ * "application/xml", * "application/json", * "application/x-www-form-urlencoded" * }, * produces={"application/xml", "application/json"}, * @SWG\Parameter( * description="ID of pet to return", * in="path", * name="petId", * required=true, * type="integer", * format="int64" * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Pet") * ), * @SWG\Response( * response="400", * description="Invalid ID supplied" * ), * @SWG\Response( * response="404", * description="Pet not found" * ), * security={ * {"api_key": {}}, * {"petstore_auth": {"write:pets", "read:pets"}} * } * ) */ public function doSomethingSmart() { return failItAllAndReturn500Response(); }
ここで、コントローラーに5つのメソッドがあり、それぞれに最大10行があると想像してください。 コードに対するコメントの割合は意気消沈するでしょう。
- ドキュメントの関連性
この例は別の欠点を示しています-ドキュメントはコードの実際の動作に依存しません。 このメソッドは常にコード500の応答を返すという事実にもかかわらず、回答はドキュメント200、400、404に表示されます。
解決策
テストのカバレッジが非常に良いので、ソリューションはそれ自体を提案します-テストはとにかく必要なすべてのスクリプトを実行します。 テストに合格する過程で、コードと回答の例、ルートのリスト、入力パラメーター、およびそれらの検証ルールを収集できます。 言い換えれば、ほとんどのドキュメント。 残りは単なる発言です。 実際、私たちはこのツールをほぼ2年前に作成しましたが、すべての手がそれについての記事を書くことはできませんでした。
実装
全体の動作原理は、ミドルウェアパターン、つまり仲介者に基づいています。 ルートごとに、仲介者のリストを構成できます。 各リクエストは、コントローラに入る前に、それぞれが何かスマートな(またはそうでない)ことを行うことができる中間チェーンを通過します。
public function handle($request, Closure $next) { $response = $next($request); if ((config('app.env') == 'testing')) { $this->service->addData($request, $response); } return $response; }
このコードは、テスト中にプラグインが情報の収集を開始することを示しています。 サービスは、要求と応答から必要な情報を収集します。 要求は、現在のアクションが実行されるルートと同様に、URIとヘッダーを返します。 唯一の問題は、検証ルールを取得することです。 ミドルウェアには、 Illuminate\Http\Request
クラスのインスタンスがあり、そこから検証データを取得することはできません。 したがって、検証のために、要求メソッドをコントローラーメソッドに「注入」することをお勧めします。
たとえば、このように
public function someAction(MyAwersomeRequest $request) { ..... }
どのコントローラーのどのメソッドを呼び出す必要があるかがわかれば、どのリクエストがそこに注入されるのかを知ることができます。 特定の要求クラスを知っていると、検証ルールを取得できます。 要求クラスへの注釈では、完全で短い説明、応答コードの説明、およびパラメーターの説明を入れることが適切であるように思えました。
アイデアの要点は、アノテーションは必須の要素ではなく、より正確な説明であるべきであるという事実に帰着します。
申込み
このプラグインの機能を実証するには、テストプロジェクトを作成します。リンクは記事の下部にあります。 この記事の一部として、段階的なドキュメントを見ていきます。
テストコントローラーを作成しましょう。
/app/Http/Controllers/TestController.php
class TestController extends Controller { public function lists() { $data = [ 'some' => 'complex', 'structure' => [ 'with' => 'multiple', 'nesting' => [] ] ]; return response()->json($data); } ... }
ご覧のとおり、このメソッドは単純にjsonオブジェクトを返します。 次に、コントローラーのリストメソッドが呼び出されるルートを登録する必要があります。
/routes/api.php
... Route::get('/test', ['uses' => TestController::class . '@lists']);
ミドルウェアAutoDocプラグインを適宜適用します
/app/Http/Kernel.php
protected $middlewareGroups = [ 'api' => [ ... AutoDocMiddleware::class ], ];
テストするには、次のテストを作成しましょう
class ExampleTest extends TestCase { public function testGetList() { $this->json('get', '/api/test'); $this->assertResponseOk(); } }
最後のテストとテストの成功を追跡するために、特別なAutoDocTestCase
作成しました。 プラグインを正しく機能させるには、TestCaseを相続人にするか、親TestCaseのtearDown
メソッドに次のコードを追加する必要があります。
public function tearDown() { $currentTestCount = $this->getTestResultObject()->count(); $allTestCount = $this->getTestResultObject()->topTestSuite()->count(); if (($currentTestCount == $allTestCount) && (!$this->hasFailed())) { $autoDocService = app(SwaggerService::class); $autoDocService->saveProductionData(); } parent::tearDown(); }
テストを実行した後、config config/auto-doc.php
ドキュメントに指定されたルートの収集されたドキュメントを見ることができます
次のようになります。
ご覧のとおり、完全な説明も簡単な説明もありません。すべてが乾燥しています。リクエストがあり、メソッドがあり、答えがあります。 このコードからこれ以上何も取得できません(部分的に他に何もないため)。 を介してリクエストを作成しましょう
php artisan make:request TestGetRequest
class TestGetRequest extends Request { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'not-found' => 'boolean', 'need-authorization' => 'boolean', 'test-parameter' => 'integer' ]; } }
さまざまな答えをシミュレートするために、特別に見つからない、承認が必要な、テストパラメーターパラメーターがあります。
これが期待どおりに機能するように、コントローラーのメソッドにいくつかのチェックを追加しましょう
public function lists(TestGetRequest $request) { if ($request->input('not-found')) { return response()->json([ 'error' => 'entity not found' ], Response::HTTP_NOT_FOUND); } if ($request->input('need-authorization')) { return response()->json([ 'error' => 'authorization failed' ], Response::HTTP_UNAUTHORIZED); } return response()->json([ 'some' => 'complex', 'structure' => [ 'with' => 'multiple', 'nesting' => [] ] ]); }
あとは、あと3つのテストを追加しましょう。
public function testGetListNotFound() { $response = $this->json('get', '/api/test', [ 'not-found' => true ]); $response->assertStatus(Response::HTTP_NOT_FOUND); } public function testGetListNoAuth() { $response = $this->json('get', '/api/test', [ 'need-authorization' => true ]); $response->assertStatus(Response::HTTP_UNAUTHORIZED); } public function testGetListWrongParameters() { $response = $this->json('get', '/api/test', [ 'test-parameter' => 'test' ]); $response->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY); }
テストを実行すると、より完全なドキュメントを見ることができます。
しかし、ここで何が欠けていますか? たとえば、リクエストの詳細な説明がない。 短い説明(テスト取得リクエスト)は、TestGetRequestクラスの再フォーマットされた名前です。 ここでは標準的な回答の説明も使用されますが、応答コードと入力パラメーターの意味を正確に指定したい場合があります。 要するに、私はしたい
プレイする。
testGetRequestクラスに注釈を追加しましょう
/** * @description * This request designed to demonstrate request with full annotation and response witch contain some data. * It has multi-line description witch will be displayed in Annotation Notes block of documentation. * It has custom summary, response code descriptions and parameters descriptions. * * @summary Test Get Request with full annotations * * @_204 This request has successfully done * @_401 You must remove need-authorization flag from input parameters for pass authorization. * @_404 We so sorry but you entity not exists. * @_422 Wrong input parameter. It must be integer * * @need-authorization If this parameter is true then you will get 401 response * @not-found If this parameter is true then you will get 404 response * @test-parameter This parameter designed for demonstrate unprocesable entity response */ class TestGetRequest extends Request {...}
注釈内のパラメーターは必要ありません。
アプリケーションレベルで応答コードの標準的な説明を設定することもできます。 これはconfig / auto-doc.phpファイルで行われます 。 説明の優先順位は次のとおりです。
- 注釈内の応答コードの説明
- configからの値
- 標準応答コードの説明
また、この構成には、ドキュメントでプロジェクトの説明を構成するために必要なものすべてが含まれています。
コマンドを実行するとき
php artisan vendor:publish
swagger-description.blade.php
ファイルは、 resources/views
フォルダーに配置されます。 たとえば、そこに次のコードを追加すると
This project designed to demonstrate working of <b>ronasit/laravel-swagger</b> plugin. Here is project description from <b>swagger-description.blade.php</b> <div class="swagger-ui"> <div class="opblock-body"> <pre> You can add some code here </pre> </div> </div> Or some image <div style="display: flex; justify-content: center; width: 100%"> <img src="/img/hqdefault.jpg"/> </div>
最終的に、ドキュメント内のプロジェクトの説明は次のようになります。
まとめ
このプラグインの主な目標は、身体の動きを最小限に抑えて統合し、ボトルネックをさらに特定できるようにすることです。 このプラグインを使用するときにプロジェクトにドキュメントが必要なのは、ドキュメント化された各ケースのテストを慎重に記述し、コントローラーメソッドではなくリクエストクラスに検証を保存することだけです。 言い換えれば、ルールへの準拠が必要であり、いずれにせよそれは遵守するのに役立ちます。