1. 程式人生 > >Laravel(PHP)使用Swagger生成API文件不完全指南 - 基本概念和環境搭建 - 簡書

Laravel(PHP)使用Swagger生成API文件不完全指南 - 基本概念和環境搭建 - 簡書

PHPer中,很多人聽說過Swagger,部分人知道Swagger是用來做API文件的,然而只有少數人真正知道怎麼正確使用Swagger,因為PHP界和Swagger相關的資料實在是太少了。所以鄙人斗膽一試,希望能以本文幫助到大家瞭解Swagger,從此告別成天用WordMarkdown折騰API文件的日子。

什麼是Swagger

Swagger is a simple yet powerful representation of your RESTful API. With the largest ecosystem of API tooling on the planet, thousands of developers are supporting Swagger in almost every modern programming language and deployment environment. With a Swagger-enabled API, you get interactive documentation, client SDK generation and discoverability.

Swagger是一種簡單、強大的RESTful API表現形式。
其擁有地球上最大的API工具生態環境,無數程式設計師在幾乎所有主流語言和開發環境中添加了對Swagger的支援。
只要你的API新增對Swagger的支援,你就等於擁有了可互動的API文件,SDK程式碼生成以及外部使用友好的API。

  • Swagger.io首頁

寫了這麼一堆貌似很屌的話,所以……Swagger到底是個啥?是一套程式碼還是一個軟體呢?

其實和官網高度概括的描述一樣,Swagger是一種通用的、和程式語言無關的API的描述規範。只要開發者在自己的API之上新增符合Swagger規範的描述,就能利用上Swagger

豐富的生態,找到符合自己開發語言的工具去做很多事:比如最常用的生成API文件,或者生成返回假資料的服務端基礎程式碼等等。

人類的文字便是一種規範,人理解了文字的規範就可以進行閱讀,機器按照規範也可以進行文字識別,武林祕籍可以寫成文字讓所有人練習,人的思想也可以寫成文字進行傳播。人類的很多經典書籍都被翻譯成了多國文字,Swagger的描述同樣也支援用YAMLXMLJSON來表示,含義都是一致的。Swagger UI通過任意一種形式的Swagger描述資訊就能渲染出酷炫的API文件,服務端介面程式碼生成工具也能通過這個描述資訊生成Controller程式碼,圍繞著Swagger,一個強大的API生態環境就出現了。

那麼就是說,以前我用Markdown寫文件,現在我用YAML/XML/JSON寫文件就行了?

是的,但Swagger功能不止如此。使用Swagger可以分為兩種情況:

  1. 專案未開始,需要先定義好API使前端和後端能夠同時開發;

  2. API已完成,需要提供文件;

第二種情況,我們確實可以直接開始編寫一個JSON或者YAML檔案來描述現有的API(使用Swagger-Editor),不過這裡我們主要介紹第一種情況,全方位感受Swagger

繼續之前,我們先介紹一下另一個東西(也許這麼多新概念就是把大家弄暈了的原因……找到Swaager的應用最佳實踐需要一點點時間,請繼續看)。

Annotation

AnnotationJava引入的一個概念,簡單來說就是用來給對應的原始碼新增額外的元資料(關於資料的更詳細的資料)的一種語法。Annotation本身不影響程式碼的執行,只能通過額外的工具解析或執行,通常用來提供程式碼檢查、資料型別標註等功能。由於Annotation的強大,很多程式語言都引入了這個概念,包括PHP

那麼,如果我們能將Swagger的描述用Annotation的方式直接寫在Controller上,豈不是既方便又好維護?

結合Swagger和PHP

PHP中使用Swagger,我們需要一個工具去編寫和解析AnnotationSwagger的描述(例如JSON形式),Swagger豐富的生態不是吹的,這裡我們直接使用前人寫好的swagger-php。而編寫API我們則使用Laravel框架(5.1)。當然,swagger-php本身和用哪種框架開發是沒關係的。

首先在Laravel專案中安裝swagger-php

$ composer require zircote/swagger-php

接著定義一個Controller用來測試:

$ artisan make:controller SwaggerController --plain

Controller中加上兩個方法:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;

class SwaggerController extends Controller
{
    /**
     * 返回JSON格式的Swagger定義
     */
    public function getJSON()
    {

    }

    /**
     * 假設是專案中的一個API
     */
    public function getMyData()
    {

    }

}

相應的,我們增加路由配置:

<?php

Route::group(['prefix' => 'swagger'], function () {
    Route::get('json', '[email protected]');
    Route::get('my-data', '[email protected]');
});

我們先實現getJSON方法,使其能夠返回合法的Swagger定義:

<?php

// ...

    /**
     * 返回JSON格式的Swagger定義
     *
     * 這裡需要一個主`Swagger`定義:
     * @SWG\Swagger(
     *   @SWG\Info(
     *     title="我的`Swagger`API文件",
     *     version="1.0.0"
     *   )
     * )
     */
    public function getJSON()
    {
        // 你可以將API的`Swagger Annotation`寫在實現API的程式碼旁,從而方便維護,
        // `swagger-php`會掃描你定義的目錄,自動合併所有定義。這裡我們直接用`Controller/`
        // 資料夾。
        $swagger = \Swagger\scan(app_path('Http/Controllers/'));

        return response()->json($swagger, 200);
    }

// ...

訪問一下/swagger/json,如果我們看到下面的返回就說明搭建成功了:

{"swagger":"2.0","info":{"title":"\u6211\u7684`Swagger`API\u6587\u6863","version":"1.0.0"},"paths":{},"definitions":{}}

然後我們來定義專案中的API介面:

<?php 

// ...

    /**
     * 假設是專案中的一個API
     *
     * @SWG\Get(path="/swagger/my-data",
     *   tags={"project"},
     *   summary="拿一些神祕的資料",
     *   description="請求該介面需要先登入。",
     *   operationId="getMyData",
     *   produces={"application/json"},
     *   @SWG\Parameter(
     *     in="formData",
     *     name="reason",
     *     type="string",
     *     description="拿資料的理由",
     *     required=true,
     *   ),
     *   @SWG\Response(response="default", description="操作成功")
     * )
     */
    public function getMyData()
    {
        //todo 待實現
    }

// ...

雖然API還沒有真正實現,但是我們的第一個API文件已經完成了(後面我們在講每個Annotation的含義)!來看看現在JSON介面的輸出內容:

{"swagger":"2.0","info":{"title":"\u6211\u7684`Swagger`API\u6587\u6863","version":"1.0.0"},"paths":{"\/swagger\/my-data":{"get":{"tags":["project"],"summary":"\u62ff\u4e00\u4e9b\u795e\u79d8\u7684\u6570\u636e","description":"\u8bf7\u6c42\u8be5\u63a5\u53e3\u9700\u8981\u5148\u767b\u5f55\u3002","operationId":"getMyData","produces":["application\/xml","application\/json"],"parameters":[{"name":"reason","in":"formData","description":"\u62ff\u6570\u636e\u7684\u7406\u7531","required":true,"type":"string"}],"responses":{"default":{"description":"\u64cd\u4f5c\u6210\u529f"}}}}},"definitions":{}}

Swagger-UI

當然,如果直接把這個甩給前端同學,他們一定會一臉懵逼看著你的……為了把JSON定義轉化為可以閱讀和互動的API,我們還需要用到Swagger-UISwagger-UI是一套不依賴後端的純HTML程式,只需要將之前輸出JSON的介面地址給它,就能得到一個強大的API文件了。為了避免請求跨域問題,我們將Swagger-UI部署在專案的public/swagger-ui/資料夾中。安裝非常簡單,直接在GitHub上下載壓縮包,解壓縮之後將dist/資料夾下的所有內容放到public/swagger-ui/就行了。

在訪問之前,我們先改一下Swagger-UI預設請求的介面地址,開啟public/swagger-ui/index.html,找到以下程式碼:

<script type="text/javascript">
    $(function () {
        var url = window.location.search.match(/url=([^&]+)/);
        if (url && url.length > 1) {
          url = decodeURIComponent(url[1]);
        } else {
          url = "http://petstore.swagger.io/v2/swagger.json"; // 就是這一行
        }

// ...

url改成我們自己介面的地址,例如http://my.project/swagger/json。開啟瀏覽器,訪問/swagger-ui/,就能看到API文件了:


高大上的介面

在這個高大上的API文件上稍微玩一會兒,你可能冒出幾個問題:

  • 有沒有中文支援?

  • 每次都要點一次才能展開API列表好麻煩,能預設展開嗎?

  • 右下角那個ERROR是什麼意思?

可能你還有更多問題,但我們先說這三個吧…

設定中文

Swagger-UI支援多國語言,還是開啟swagger-ui/index.html,大約在30行左右:

// ...

    <!-- Some basic translations -->
    <!--<script src='lang/translator.js' type='text/javascript'></script>-->
    <!--<script src='lang/ru.js' type='text/javascript'></script>-->
    <!-- <script src='lang/en.js' type='text/javascript'></script> -->

// ...

我們改為:

// ...

    <!-- Some basic translations -->
    <script src='lang/translator.js' type='text/javascript'></script>
    <script src='lang/zh-cn.js' type='text/javascript'></script>

// ...

這樣就會變成中文了:


親切的中文介面

API列表預設展開

大概在swagger-ui/index.html的74行左右有以下程式碼:

// ...

    onFailure: function(data) {
      log("Unable to Load SwaggerUI");
    },
    docExpansion: "none", // 這一行就是用來設定文件預設展開層級的
    jsonEditor: false,

// ...

這個配置有三個值:

  • none 摺疊所有內容

  • list 展開所有分組的API列表

  • full 展開所有分組的API列表以及每個API的請求細節

一般來說設定為list就可以了。

去掉ERROR提示

Swagger-UI預設會將你的介面JSON傳給swagger.io進行格式驗證,然後對於我們已經使用了swagger-php的專案來說基本不需要(因為寫錯了Annotation的話會造成Swagger JSON介面報錯),而且內部專案有時也不方便暴露,所以我們可以關閉驗證功能來去除右下角的ERROR提示圖示。這個配置並不存在於swagger-ui/index.html中,我們需要手動在Swagger UI宣告時設定一個新引數:

// ...

    window.swaggerUi = new SwaggerUi({
        // ...

        validatorUrl: null, //新增這個配置
    });

// ...

再次重新整理之後驗證提示就徹底消失了。

如果你需要這個驗證提示,但又不想使用swagger.io的公用服務,你也可以自己搭建一個,點這裡檢視Swagger Validator Badge的使用方法

折騰成果


折騰後的成果

自定義Swagger UI

Swagger UI本身提供了很多配置和引數供使用者自定義,點這裡檢視,大家可以隨意發揮。

待續

下次再寫關於swagger-php的使用細節。

延伸閱讀

在PHP中寫複雜的Swagger定義時如何偷懶(基於zircote/swagger-php)