swagger 產生 PHP restful API 介面文檔____PHP

來源:互聯網
上載者:User
需求:


為用戶端同事寫介面文檔的各位後端同學,已經在各種場合回憶了使用自動化文檔工具前手寫文檔的血淚史.

我的故事卻又不同,因為首先來說,我在公司是 Android 組負責人,屬於上述血淚史中催死人不償命的用戶端陣營.

但血淚史卻是相通的,沒有自動化文檔的日子,對介面就是開發流程中最低效的環節.

因此決定使用 swagger 搭建由php注釋產生文檔的流程.


背景:

我們的 restful api 項目採用 phalcon 架構,整體結構很簡單,我們只需要用 swagger 掃描 controller 目錄即可.

下簡稱我們的 php api 項目為 php_api_project.

伺服器採用 nginx.


搭建:


先說下最終的文檔產生流程會是什麼樣子,以便先有個整體的認識:

搭建完成後, 整個流程, 從文檔產生到前端展現, 大體如下:

1. 在php檔案中寫 swagger 格式的 /** 注釋 */

2. 用 swagger-php 內的 bin/swagger.phar 命令掃描 php controller 所在目錄, 產生 swagger.json 檔案

3. 將 swagger.json 檔案拷貝到 swagger-ui 中 index.html 指定的目錄中

4. 開啟 swagger-ui 所在的 url, 就可以看到文檔了. 文檔中的各個 api 可以在該網址上直接存取得到資料.


實現此需求只需要 swagger 的如下兩個項目:

swagger-php: 掃描 php 注釋的工具. 內含一個不錯的例子.

swagger-ui: 用以將掃描工具產生的 swagger.json 檔案內容展示在網頁上.

首先將這兩個項目下載到本地:

$ git clone https://github.com/swagger-api/swagger-ui.git$ git clone https://github.com/zircote/swagger-php.git

文檔產生工具部署:


說是部署,主要就是產生 bin/swagger 這個用來產生 swagger.json 檔案的命令.

主要工作,就是用 composer 解決下依賴就可以了.

因為國內直接用 composer 比較蛋疼,所以最好設定下國內的那個 composer 源.

這樣的話, 整個 文檔產生工具的部署 就是下面三行命令:

$ cd swagger-php$ composer config repo.packagist composer https://packagist.phpcomposer.com$ composer update

只要中間不報錯,就算部署完成了. 完成後可以產生一份文檔試一下.

swagger-php 項目下的 Examples 目錄下有一個樣本php工程,裡面已經用 swagger 格式寫了各種介面注釋, 我們來嘗試產生一份文檔.

執行下面命令:

$ cd swagger-php$ mkdir json_docs$ php ./bin/swagger ./Examples -o json_docs/

上面命令會掃描 Examples 目錄中的php檔案注釋, 然後在 json_docs 目錄下產生 swagger.json 檔案.

這個 swagger.json 檔案就是前端 swagger-ui 用來展示的我們的api文檔檔案.

NOTE: swagger-php 只是個工具,放在哪裡都可以.


前端 swagger-ui 部署:


部署方法很簡單,就三步:


1. 將 swagger-ui 項目中的 dist 檔案夾拷貝到 php_rest_api 根目錄下.


NOTE1: 只需要拷貝dist這一個檔案夾就可以了.最好重新命名下,簡單起見,這裡不再重新命名.

NOTE2: 我們的項目根目錄和 nginx 配置的 root 是同一個目錄.其實不用放跟目錄,只要放到一個不用跨域就跨域訪問的目錄就可以了. 為啥有跨域問題? 後面會講.


2. 修改 dist 檔案夾下的 index.html 檔案,指定 swagger.json 所在目錄


只改一行就可以.

簡單起見,這裡直接將 swagger.json 目錄指定在 dist 目錄下即可. 我們這裡屢一下預設條件:

假設 php_api_project 項目的 host 是 api.my_project.com;

假設 php_api_project 項目在 nginx 中指定的 root 即為其根目錄;

假設 swagger-ui 裡的 dist 檔案夾放在上述根目錄中;

假設 swagger.json 檔案就打算放在上述 dist 目錄下 (php_api_project/dist/swagger.json) ;

那麼 index.html 中把下面的片段改成這樣:

      var url = window.location.search.match(/url=([^&]+)/);      if (url && url.length > 1) {        url = decodeURIComponent(url[1]);      } else {        <!-- 就是這行,改成你產生的 swagger.json 可以被訪問到的路徑即可 -->        url = "http://api.my_project.com/dist/swagger.json";      }

3. 拷貝 swagger.json 到上述目錄中.

# 把 swagger-php_dir 這個,換成你的 swagger-php 錄即可cp swagger-php_dir/json_docs/swagger.json php_api_project/dist/


上述步驟完成後, 訪問 http://api.my_project.com/dist/index.html 就可以看到 Examples 那個小項目的 api 文檔了.


編寫 PHP 注釋:


swagger-php 項目的 Example 中已經有了很多相關例子,照著複製粘貼就可以了.

更具體的相關注釋規則的文檔,看這裡:

http://bfanger.nl/swagger-explained/#/swaggerObject


假設我的項目 controller 所在目錄為 php_api_project/controller/, 那麼我只需要掃描這個目錄就可以了,不用掃描整個 php 工程.

為了在 swagger.json 中產生某些統一的配置, 建立 php_api_project/controller/swagger 目錄. 目錄存放一個沒有代碼的php檔案,裡面唯寫注釋.

我給這個檔案取名叫 Swagger.php, 大體內容如下:

<?php/** * @SWG\Swagger( *   schemes={"http"}, *   host="api.my_project.com", *   consumes={"multipart/form-data"}, *   produces={"application/json"}, *   @SWG\Info( *     version="2.3", *     title="my project doc", *     description="my project 介面文檔, V2-3.<br>以後大家就在這裡愉快的對介面把!<br>以後大家就在這裡愉快的對介面把!<br>以後大家就在這裡愉快的對介面把!<br>" *   ), * *   @SWG\Tag( *     name="User", *     description="使用者操作", *   ), * *   @SWG\Tag( *     name="MainPage", *     description="首頁模組", *   ), * *   @SWG\Tag( *     name="News", *     description="新聞資訊", *   ), * *   @SWG\Tag( *     name="Misc", *     description="其他介面", *   ), * ) */

如上所示,我的這個php檔案一行php代碼也沒有,就只有注釋,為了定義一些全域的swagger設定:

schemes: 使用協議 (可以填多種協議)

host: 項目地址, 這個地址會作為每個介面的 url base ,拼接起來一期作為訪問地址

consumes: 介面預設接收的MIME類型, 我的例子中的 formData 對應post表單類型. 注意這是項目預設值,在單個介面注釋裡可以複寫這個值.

produces: 介面預設的回複MIME類型. api介面用的比較多的就是 application/json 和 application/xml.

@SWG\Info: 這個裡面填寫的東西,會放在文檔的最開頭,用作文檔說明.

@SWG\Tag: tag是用來給文檔分類的,name欄位必須唯一.某個介面可以指定多個tag,那它就會出現在多組分類中. tag也可以不用在這裡預先定義就可以使用,但那樣就沒有描述了. 多說無益,稍微用用就啥都明白了.

然後就是給每個介面編寫 swagger 格式的注釋了.還是舉個栗子吧:

    /**     * @SWG\Post(path="/user/login", tags={"User"},     *   summary="登入介面(使用者名稱+密碼)",     *   description="使用者登入介面,帳號可為 使用者名稱 或 手機號. 參考(這個會在頁面產生一個可跳轉的連結: [使用者登入注意事項](http://blog.csdn.net/liuxu0703/)",     *   @SWG\Parameter(name="userName", type="string", required=true, in="formData",     *     description="登入使用者名稱/手機號"     *   ),     *   @SWG\Parameter(name="password", type="string", required=true, in="formData",     *     description="登入密碼"     *   ),     *   @SWG\Parameter(name="image_list", type="string", required=true, in="formData",     *     @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Image")),     *     description="使用者相簿. 好吧,沒人會在登入時要求填一堆圖片資訊.這裡是為了樣本 帶結構的資料, @SWG\Schema ,這個結構需要另行定義,下面會講."     *   ),     *   @SWG\Parameter(name="video", type="string", required=true, in="formData",     *     @SWG\Schema(ref="#/definitions/Video"),     *     description="使用者 呃... 視頻? 同上,為了樣本 @SWG\Schema ."     *   ),     *   @SWG\Parameter(name="client_type", type="integer", required=false, in="formData",     *     description="調用此介面的用戶端類型: 1-Android, 2-IOS. 非必填,所以 required 寫了 false"     *   ),     *   @SWG\Parameter(name="gender", type="integer", required=false, in="formData",     *     default="1",     *     description="性別: 1-男; 2-女. 注意這個參數的default上寫的不是參數預設值,而是預設會被填寫在swagger頁面上的值,為的是方便用swagger就地訪問該介面."     *   ),     * )     */    public function loginAction() {        // php code    }     /**     * @SWG\Get(path="/User/myWebPage", tags={"User"},     *   produces={"text/html"},     *   summary="使用者的個人網頁",     *   description="這不是個api介面,這個返回一個頁面,所以 produces 寫了 text/html",     *   @SWG\Parameter(name="userId", type="integer", required=true, in="query"),     *   @SWG\Parameter(name="userToken", type="string", required=true, in="query",     *     description="使用者令牌",     *   ),     * )     */    public function myWebPageAction(){        // php code    }

規則簡單明了,看著代碼大家就都懂了.不懂的話,去看文檔吧...

上面 login 介面中用到了兩個有結構的資料, 一個是 image 類型的數組, 一個是 video 類型的結構.

(其實結構化的參數只能在 in="body" 時才可以用,但這並不妨礙我們為了簡化問題,把結構化資料格式化為 json 當字串傳遞. 我們只要將這種結構展現在文檔裡就可以了)

這種有結構的東西 swagger 也可以用 php 注釋定義:

<?php/** * @SWG\Definition(type="object", @SWG\Xml(name="Image")) */class Image {    /**     * @SWG\Property()     * @var string     */    public $url;        /**     * @SWG\Property(format="int32")     * @var int     */    public $height;        /**     * @SWG\Property(format="int32")     * @var int     */    public $width;}

<?php/** * @SWG\Definition(type="object", @SWG\Xml(name="Video")) */class Video {    /**     * @SWG\Property()     * @var string     */    public $url;    /**     * @SWG\Property()     * @var string     */    public $thumb_url;    /**     * @SWG\Property(format="int32")     * @var int     */    public $length;    /**     * @SWG\Property(format="int64")     * @var int     */    public $size;}

這樣當這兩個類也被 swagger-php/bin/swagger 掃描到後,其他地方就可以正確引用到 Image 和 Video 為名字的這兩個結構體了.

這樣做的好處是,在介面參數文檔中,這個結構會被展示出來,這樣用戶端同學就知道該傳什麼結構了.


我的介面栗子裡都沒有寫 response 規則,是因為我們用 json 作為返回載體,返回錯誤碼也是包含在這個 json 結構體裡. 並且多數介面返回的json格式都很複雜,用 swagger 的 response 規則基本沒法描述.

swagger 的 response 編寫規則是按照 http 的 response code 來的 (404, 401 等), 總之對我們的介面來說,這套描述規則不好用.

因此我就直接捨棄了 response 描述, 直接用 swagger 就地請求介面看看返回了什麼就是. 再不行就把介面的返回資訊在 description 裡大體描述一下.


文檔寫完後,就可以調用 swagger-php/bin/swagger 命令產生 swagger.json, 再拷貝到 swagger-ui 中你指定的那個目錄中,就可以訪問文檔了.


NOTE:

大家應該已經看出來了,其實介面的注釋不一定要寫在介面上,憑空寫注釋,一樣能產生文檔.所以不必糾結各個注釋放在什麼地方. 比如 swagger 整體定義, tag 定義等,寫在任意可以被掃描到的 php 檔案中就可以了.


常用欄位簡要說明:


這裡只是自己理解加翻譯的簡要說明,更詳細的欄位說明,還是要去看文檔.再次貼出文檔:
http://bfanger.nl/swagger-explained/#/swaggerObject


介面描述 (@SWG\Get, @SWG\Post 等) 常用欄位:

summary - string介面的簡要介紹,會顯示在介面標題上,不能超過120個字元description - string介面的詳細介紹externalDocs - string外部文檔連結operationId - string全域唯一的介面標識consumes - [string]介面接收的MIME類型produces - [string]介面返回的MIME類型,如 application/jsonschemes -[string]介面所支援的協議,取值僅限: "http", "https", "ws", "wss"parameters -[Parameter Object | Reference Object]參數列表

參數描述 (@SWG\Parameter) 常用欄位:

name - string參數名. 通過路徑傳參(in 取值 "path")時有注意事項,沒用到,懶得看了...in - string參數從何處來. 必填. 取值僅限: "query", "header", "path", "formData", "body"description - string參數描述. 最好別太長type - string參數類型. 取值僅限: "string", "number", "integer", "boolean", "array", "file"required - boolean參數是否必須. 通過路徑傳參(in 取值 "path")時必須為 true.default - *預設值. 在你打算把參數通過 path 傳遞時規矩挺多,我沒用到.用到的同學自己看文檔吧.


遇到的問題:


跨域問題:


swagger 牛X的地方就是它可以在文檔上就地提供者並展示輸出,對於調試和對介面非常的方便.但如果不想將 swagger-ui 部署在介面項目下,那麼在 swagger-ui 就地提供者時,就會因跨域問題而請求不到結果. (Response Headers: no response from server; Response Body: no content).

這裡不講跨域問題怎麼解決,只是給遇到上面的問題的各位一個思路,知道錯誤是由跨域產生的,就好解決了.


登入鑒權:


如果要將文檔放在公網,直接暴露自己的介面可不太合適. 因此要給文檔的訪問地址做鑒權.

因為安全性要求不高,並且公司較小開發組人員不多,我就直接用了 nginx 提供的 http basic auth 做了登入鑒權.

老規矩,先貼官方文檔:

https://www.nginx.com/resources/admin-guide/restricting-access-auth-basic/

這樣做鑒權很簡單,分分鐘搞定. 不詳述, 就兩步:


1. 首先用 htpasswd 命令,為需要訪問文檔的同學產生帳號名和密碼:

$ htpasswd -cb your/path/to/api_project_accounts.db admin password_for_admin$ htpasswd -b your/path/to/api_project_accounts.db liuxu 123456$ htpasswd your/path/to/api_project_accounts.db xiaoming

-c 選項表示如果帳號檔案 ( api_project_accounts.db ) 不存在,則建立之. 因此建立第一個帳號時一定要加 -c, 之後建立則一定不要再加 -c.

-b 參數表示明文指定密碼,就是上面第一條和第二條命令中最後一個輸入 (password_for_admin, 123456) . 因此如果不想明文指定,可以不加 -b, 像上面的第三條命令,就會和sudo命令一樣,讓你輸入一段看不見的密碼.

上面命令建立了三個帳號, admin, liuxu, xiaoming, 並將帳號密碼儲存在 api_project_accounts.db 檔案中.

2. 然後在 nginx 的項目配置中給自己的這個訪問地址開啟 http basic auth 就可以了.

    location /dist {        auth_basic              "my api project login";        auth_basic_user_file    your/path/to/api_project_accounts.db;    }

記得改完了重啟 nginx:

nginx -s reload

這樣一個簡單的登入鑒權就建立起來了.


附錄:


swagger 官網:

http://swagger.io/


swagger 項目地址:

https://github.com/swagger-api

https://github.com/swagger-api/swagger-ui

swagger php 是個單獨的項目:

https://github.com/zircote/swagger-php


swagger 文檔參考地址:

http://bfanger.nl/swagger-explained/#/swaggerObject


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.