Swagger 怎麼寫,實戰演練,讓我來一步一步教你…
本文大綱
目錄
前言
上一篇 [比較導入 Api 文件選擇 Swagger or apidocs] ,文中說明為什麼要導入 Api 文件的原因、要解決的問題,並比較兩款工具的優缺點,選擇導入 Swagger 的原因為何?
這一篇會去紀錄筆者學習 Swagger 文件寫法的雷,並帶出範例,讓讀者可以在應用上能少走一些冤枉路!
Swagger 參數設定
筆者團隊使用 darkaonline/l5-swagger: "6.*"
,用 composer 進行安裝吧!
此外,VScode extension: Swagger-PHP Annotation v1.1.1
可自動生成標準語法,節省時間。
1 2 3 4 5 6 7 8
composer require darkaonline/l5-swaggerc
php artisan vendor:publish --provider L5Swagger\L5SwaggerServiceProvider // command line
L5Swagger\L5SwaggerServiceProvider::class // config/app.php 加入此ServiceProvider
// app/Provider/AppServiceProvider
public function register()
{
$this->app->register(\L5Swagger\L5SwaggerServiceProvider::class);
}
接下來執行 php artisan l5-swagger:generate
就可以產生文件囉!
如有遇到問題,可能是你 app/Http/Controllers/Controller.php
沒有加上 OA
規範。詳細可以參考這篇
Controller.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/**
* @OA\OpenApi(
* @OA\Info(
* title="Swagger-doc Services API",
* version="1.0.0",
* description="Swagger Service App",
* @OA\Contact(
* email="[email protected] "
* )
* ),
* @OA\Server(
* description="Swagger-doc App API",
* url="https://your-server-domain/api/document"
* ),
* @OA\PathItem(
* path="/"
* )
* )
*/
再看一次 Swagger 提供的 Live demo ,這是我們的目標,產出這麼美的文件。
swagger 精美畫面
swagger get
大補帖
swagger array 要用 {} 替代 []
tags 代表 Api 分類
path 你的 Api 路徑,也可以於 url 代入參數
summary 描述 Api
@OA\Parameter
說明 url 的 path or query 如何定義
@OA\Property
說明此 Api 屬性屬性,可以用於 Schema, RequestBody, Response 等說明欄位屬性與型態
@OA\Response
描述回傳 response 定義、格式、型態與範例
@OA\MediaType
用來描述內容,可用於 response 內容描述
@OA\Examples
說明不同的情境示範參數
@OA\Schema
定義欄位格式,也可以將規範寫在 model 內,然後引用在 Controller 內,例如:@OA\Schema(ref="#/components/schemas/Promotion")
以下面為 Prmotion 促銷為例,如 requestBody & Response 引用此 Schema 可以讓使用者馬上了解其格式。
required 如 POST/Patch 必填
example 範例參數
readOnly 是否開放更新
format 格式
Promotion.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
/**
* @OA\Schema(
* schema="Promotion",
* required={"id", "name", "property-1", "property-2", "property-3"},
* @OA\Property(
* property="id",
* description="id",
* type="integer",
* format="int32",
* example="1",
* readOnly=true
* ),
* @OA\Property(
* property="name",
* description="name",
* type="string",
* format="string",
* example="促銷方案"
* ),
* @OA\Property(
* property="property-1",
* description="property-1",
* type="integer",
* format="int32",
* example="618",
* ),
* @OA\Property(
* property="property-2",
* description="property-2",
* type="string",
* format="float",
* example="屬性二"
* ),
* @OA\Property(
* property="property-3",
* description="property-3",
* type="integer",
* format="integer",
* example="25",
* ),
* @OA\Property(
* property="_method",
* type="string",
* description="如為 Update 請幫我填上 PATCH",
* example="",
* ),
* )
*/
class Promotion extends Model
{
}
@OA\JsonContent
用來描述內容,可用於 response 內容描述、格式定義,例如:@OA\JsonContent(ref="#/components/schemas/User")
也可以自定義方式:
PromotionController.php
1 2 3 4 5
* @OA\Response(
* response=200,
* description="successful operation",
* @OA\JsonContent(ref="#/components/schemas/Promotion"),
* )
Controller.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
* @OA\Response(response=200, description="Successful operation",
* content={
* @OA\MediaType(
* mediaType="application/json",
* example=
* {
* "code": 200,
* "message": "",
* "data": {
* "id": 999,
* "text": "Swagger Api"
* }
* }
* )
* }
* ),
Request Body:定義 payload 要傳的參數與格式,也提供兩種寫法,給讀者參考,取決於 Api 情境與參數形式
寫法一:用於 Post / Patch 有明確定義的 Schema
PromotionController.php
1 2 3 4 5 6
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="multipart/form-data",
* @OA\Schema(ref="#/components/schemas/Promotion")
* )
* )
PromotionController.php
1 2 3 4 5 6 7 8 9 10
* @OA\RequestBody(
* @OA\JsonContent(
* type="object",
* @OA\Property(property="id", type="integer", default="999"),
* @OA\Property(property="emails", type="arrays", default={"[email protected] ", "[email protected] "}),
* examples = {
* @OA\Examples(example="範例", summary="多個 emails", value={"id": "999", "emails": {"[email protected] ", "[email protected] "}}),
* }
* )
* ),
有些 Api 會設定特定 token 才可有權限取得其資料,其設定可谷歌搜尋一下,也可參照這篇
l5-swagger.php
須設定 bear_token
l5-swagger.php
1 2 3 4 5 6
'bearer_token' => [ // Unique name of security
'type' => 'apiKey', // Valid values are "basic", "apiKey" or "oauth2".
'description' => 'Enter token in format (Bearer <token>)',
'name' => 'Authorization', // The name of the header or query parameter to be used.
'in' => 'header', // The location of the API key. Valid values are "query" or "header".
],
Promotion.php
1 2 3 4 5 6 7 8
/**
* @OAS\SecurityScheme(
* securityScheme="bearer_token",
* type="http",
* scheme="bearer"
* )
*/
clas PromotionController extends Controller
每個 Api docs 設定 security security={{"bearer_token":{}}},
Swagger 示範寫法
Get 寫法
以下是 Get 寫法,搭配 summary 介紹,可以知道這隻 Api 拿到商品資訊。
PromotionController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/**
* @OA\Get(
* tags={"Products"},
* path="/your/api/path/{id}",
* summary="拿到商品相關資訊",
* @OA\Parameter(parameter="productId",in="path",name="productId",required=true,description="input your productID"),
* @OA\Response(response=200, description="OK",
* content={
* @OA\MediaType(
* mediaType="application/json",
* example={
* {
* "code": 200,
* "message": "",
* "data": {
* "id": 20,
* "image_url": "https://url.com.tw/your_image_path",
* "name": "your-product-name",
* "price": 333
* }
* }
* }
* )
* }
* ),
* @OA\Response(response=204, description="Product information is null")
* )
*/
Create 寫法
筆者以 Create Promotion 為例,去撰寫以下 Swagger。
PromotionController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/**
* @OA\POST(
* tags={"Promotions"},
* path="/your/api/path/promotion",
* summary="create promotion",
* security={{"bearer_token":{}}},
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="multipart/form-data",
* @OA\Schema(ref="#/components/schemas/Promotion")
* )
* ),
* @OA\Response(response=201, description="Created successfully",
* content={
* @OA\MediaType(
* mediaType="application/json",
* example={
* {
* "code": 201,
* "message": "",
* "data": {
* "update_type": "define-on-you",
* "outcome": true
* }
* }
* }
* ),
* }
* ),
* @OA\Response(response=401, description="Unauthorized"),
* )
*/
public function createPromotion(Request $request)
{
}
Update(Patch) 寫法
這邊我示範三個範例,第一個範例是應用 Schema 寫法,筆者只需要在 Model 定義好每個欄位型態與屬性即可,後續引用 @OA\Schema(ref="#/components/schemas/Promotion")
,即可應用 Promotion 定義的欄位。
PromotionController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/**
* @OA\POST(
* tags={"Promotions"},
* path="/your/api/path/{promotionId}",
* summary="update promotion by promotionID",
* @OA\Parameter(parameter="promotionId",in="path",name="promotionId",required=true,description="input your promotionId",
* @OA\Schema(type="integer")
* ),
* security={{"bearer_token":{}}},
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="multipart/form-data",
* @OA\Schema(ref="#/components/schemas/Promotion")
* )
* ),
* @OA\Response(response=204, description="Updated successfully",
* content={
* @OA\MediaType(
* mediaType="application/json",
* example={
* {
* "code": 204,
* "message": "",
* "data": {
* "update_type": "type",
* "outcome": true
* }
* }
* }
* )
* }
* ),
* @OA\Response(response=401, description="Unauthorized"),
* )
*/
public function updatePromotion(Request $request, int $promotionId)
{
}
範例二,情境可能應用於這隻 Api,其回傳資料格式與 Model 相差甚大,需要自定義格式輸出。
PromotionController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/**
* @OA\POST(
* tags={"Promotions"},
* path="/your/api/path/{promotionId}",
* summary="update promotion by promotionID",
* @OA\Parameter(parameter="promotionId",in="path",name="promotionId",required=true,description="input your promotionId",
* @OA\Schema(type="integer")
* ),
* security={{"bearer_token":{}}},
* @OA\RequestBody(
* @OA\MediaType(
* mediaType="multipart/form-data",
* @OA\Schema(
* @OA\Property(
* property="name",
* type="string",
* ),
* @OA\Property(
* property="your-property",
* type="string",
* ),
* @OA\Property(
* property="your-property",
* type="string",
* ),
* @OA\Property(
* property="your-property",
* type="string",
* ),
* )
* )
* ),
* @OA\Response(response=204, description="Updated",
* content={
* @OA\MediaType(
* mediaType="application/json",
* example={
* {
* "code": 204,
* "message": "",
* "data": {
* "update_type": "depend-on-what-you-define",
* "outcome": true
* }
* }
* }
* )
* }
* ),
* @OA\Response(response=401, description="Unauthorized"),
* )
*/
public function updatePromotion(Request $request, int $promotionId)
{
}
第三個示範,是發生在不是傳統的 CRUD,可能是內/外部系統帶上不同的 Request,要做不同的事,實務上經常發生,提供給讀者更多不同寫法。
以下就是這隻 Api 處理單筆與處理多筆更新,接受不同的 payload 格式,再請開發者或測試人員依照格式去發送 Request,Api 才能正常執行。
PromotionController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/**
* @OA\POST(
* tags={"Promotions"},
* path="/your/api/path/update_depend_on_request",
* summary="根據不同參數更新資料",
* security={{"bearer_token":{}}},
* @OA\RequestBody(
* @OA\JsonContent(
* type="object",
* @OA\Property(property="some-property-type", type="string", default="單筆更新/多筆更新"),
* @OA\Property(property="payload", type="json", default="{}"),
* examples = {
* @OA\Examples(example="單筆更新", summary="單筆", value={"event": "aaa", "payload": {"id": 5566, "name": "王小明", "grade": 2, "timestamp": 1657777068}}),
* @OA\Examples(example="多筆更新", summary="多筆", value={"event": "bbb", "payload": {"rows": {{"id": 5566, "name": 王小明, "grade": 2}, {"id": 1234, "name": "王大明", "grade": 5}}, "timestamp": 1657777068}})
* }
* )
* ),
* @OA\Response(response=401, description="Unauthorized"),
* @OA\Response(response=200, description="成功",
* content={
* @OA\MediaType(
* mediaType="application/json",
* example=
* {
* "code": 200,
* "message": "",
* "data": {}
* }
* )
* }
* ),
* @OA\Response(response=422, description="驗證失敗",
* content={
* @OA\MediaType(
* mediaType="application/json",
* example=
* {
* "code": 422,
* "message": {
* "payload.id": {
* "payload.id 須為 integer"
* }
* },
* "data": {}
* }
* )
* }
* ),
* @OA\Response(response=400, description="其他失敗",
* content={
* @OA\MediaType(
* mediaType="application/json",
* example=
* {
* "code": 400,
* "message": "商業邏輯處理錯誤",
* "data": {}
* }
* )
* }
* ),
* )
*/
學習重點
老實說,網路上 Swagger 撰寫語法比較雜亂,需要東拼西湊,才有辦法實現某種特定功能,特別是 Laravel 內的寫法,筆者在撰寫過程中,也是常常踩雷,去嘗試不同的語法,去實現自己想要的結果。
中文資源的 Swagger 教學比較少,筆者想透過自己撰寫的學習經驗,去補充網路上這塊資源的不足,算得上是貢獻自己的微薄之力!
希望看到這篇的讀者,對你有所幫助,藉此整理出自己 Swagger 語法與紀錄自己的學習心得,也算是一大成就!
參考連結