什麼是swagger?
Swagger是一個簡單但功能強大的API表達工具。它具有地球上最大的API工具生態系統,數以千計的開發人員,使用幾乎所有的現代程式語言,都在支援和使用Swagger。使用Swagger生成API,我們可以得到互動式文件,自動生成程式碼的SDK以及API的發現特性等。
swagger文件長啥樣?
一個最簡單的swagger文件示例:
swagger: "2.0"
info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification
schemes:
- https
host: simple.api
basePath: /openapi101
paths: {}
複製程式碼
Tips:閱讀本文前提是假設你已經瞭解瞭如何編寫swagger文件,當然,如果還不瞭解也沒關係,可以去swagger官網檢視文件進行學習,並且這裡還有一套《Swagger從入門到精通》附上.
本文背景介紹
寫作本文的原因是因為公司要求api文件都使用 swagger格式,專案是用golang編寫的,作為一個懶癌程式設計師,怎麼能夠忍受去編寫這麼複雜的swagger文件呢?有沒有一鍵生成的工具呢?google一下,還真有,那就是go-swagger專案。go-swagger眾多特色功能之一就是Generate a spec from source,即通過原始碼生成文件,很符合我的需求。
下面就簡單介紹下如何為專案加上swagger註釋,然後一鍵生成API文件
開始之前需要安裝兩個工具:
- swagger-editor:用於編寫swagger文件,UI展示,生成程式碼等...
- go-swagger:用於一鍵生成API文件
安裝swagger-editor,我這裡使用docker執行,其他安裝方式,請檢視官方文件:
docker pull swaggerapi/swagger-editor
docker run --rm -p 80:8080 swaggerapi/swagger-editor
複製程式碼
安裝go-swagger,我這邊使用brew安裝,其他安裝方式,請檢視官方文件
brew tap go-swagger/go-swagger
brew install go-swagger
複製程式碼
好了,現在終於開始正題:start coding!!!
開始編寫註釋
1.假設有一個user.server,提供一些REST API,用於對使用者資料的增刪改查。
比如這裡有一個getOneUser
介面,是查詢使用者資訊的:
package service
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"user.server/models"
"github.com/Sirupsen/logrus"
)
type GetUserParam struct {
Id int `json:"id"`
}
func GetOneUser(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
decoder := json.NewDecoder(r.Body)
var param GetUserParam
err := decoder.Decode(¶m)
if err != nil {
WriteResponse(w, ErrorResponseCode, "request param is invalid, please check!", nil)
return
}
// get user from db
user, err := models.GetOne(strconv.Itoa(param.Id))
if err != nil {
logrus.Warn(err)
WriteResponse(w, ErrorResponseCode, "failed", nil)
return
}
WriteResponse(w, SuccessResponseCode, "success", user)
}
複製程式碼
根據swagger文件規範,一個swagger文件首先要有swagger的版本和info資訊。利用go-swagger只需要在宣告package之前加上如下注釋即可:
// Package classification User API.
//
// The purpose of this service is to provide an application
// that is using plain go code to define an API
//
// Host: localhost
// Version: 0.0.1
//
// swagger:meta
package service
複製程式碼
然後在專案根目錄下使用swagger generate spec -o ./swagger.json
命令生成swagger.json
檔案:
此命令會找到main.go入口檔案,然後遍歷所有原始碼檔案,解析然後生成swagger.json檔案
{
"swagger": "2.0",
"info": {
"description": "The purpose of this service is to provide an application\nthat is using plain go code to define an API",
"title": "User API.",
"version": "0.0.1"
},
"host": "localhost",
"paths": {}
}
複製程式碼
2.基本資訊有了,然後就要有路由,請求,響應等,下面針對getOneUser介面編寫swagger註釋:
// swagger:parameters getSingleUser
type GetUserParam struct {
// an id of user info
//
// Required: true
// in: path
Id int `json:"id"`
}
func GetOneUser(w http.ResponseWriter, r *http.Request) {
// swagger:route GET /users/{id} users getSingleUser
//
// get a user by userID
//
// This will show a user info
//
// Responses:
// 200: UserResponse
decoder := json.NewDecoder(r.Body)
var param GetUserParam
err := decoder.Decode(¶m)
if err != nil {
WriteResponse(w, ErrorResponseCode, "request param is invalid, please check!", nil)
return
}
// get user from db
user, err := models.GetOne(strconv.Itoa(param.Id))
if err != nil {
logrus.Warn(err)
WriteResponse(w, ErrorResponseCode, "failed", nil)
return
}
WriteResponse(w, SuccessResponseCode, "success", user)
}
複製程式碼
可以看到在GetUserParam
結構體上面加了一行swagger:parameters getSingleUser
的註釋資訊,這是宣告介面的入參註釋,結構體內部的幾行註釋指明瞭id這個引數必填,並且查詢引數id是在url path中。詳細用法,參考: swagger:params
在GetOneUser
函式中:
swagger:route
指明使用的http method,路由,以及標籤和operation id,詳細用法,參考: swagger:routeResponses
指明瞭返回值的code以及型別
然後再宣告響應:
// User Info
//
// swagger:response UserResponse
type UserWapper struct {
// in: body
Body ResponseMessage
}
type ResponseMessage struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
複製程式碼
使用swagger:response
語法宣告返回值,其上兩行是返回值的描述(我也不清楚,為啥描述資訊要寫在上面,歡迎解惑),詳細用法,參考; swagger:response
然後瀏覽器訪問localhost
,檢視swagger-editor介面,點選工具欄中的File->Impoprt File
上傳剛才生成的 swagger.json
檔案,就可以看到介面:
這樣一個簡單的api文件就生成了
3.怎麼樣?是不是很簡單?可是又感覺那裡不對,嗯,註釋都寫在程式碼裡了,很不美觀,而且不易維護。想一下go-swagger的原理是掃描目錄下的所有go檔案,解析註釋資訊。那麼是不是可以把api註釋都集中寫在單個檔案內,統一管理,免得分散在各個原始碼檔案內。
新建一個doc.go
檔案,這裡還有一個介面是UpdateUser
,那麼我們在doc.go檔案中宣告此介面的api註釋。先看一下UpdateUser
介面的程式碼:
func UpdateUser(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
// decode body data into user struct
decoder := json.NewDecoder(r.Body)
user := models.User{}
err := decoder.Decode(&user)
if err != nil {
WriteResponse(w, ErrorResponseCode, "user data is invalid, please check!", nil)
return
}
// check if user exists
data, err := models.GetUserById(user.Id)
if err != nil {
logrus.Warn(err)
WriteResponse(w, ErrorResponseCode, "query user failed", nil)
return
}
if data.Id == 0 {
WriteResponse(w, ErrorResponseCode, "user not exists, no need to update", nil)
return
}
// update
_, err = models.Update(user)
if err != nil {
WriteResponse(w, ErrorResponseCode, "update user data failed, please try again!", nil)
return
}
WriteResponse(w, SuccessResponseCode, "update user data success!", nil)
}
複製程式碼
然後再doc.go檔案中編寫如下宣告:
package service
import "user.server/models"
// swagger:parameters UpdateUserResponseWrapper
type UpdateUserRequest struct {
// in: body
Body models.User
}
// Update User Info
//
// swagger:response UpdateUserResponseWrapper
type UpdateUserResponseWrapper struct {
// in: body
Body ResponseMessage
}
// swagger:route POST /users users UpdateUserResponseWrapper
//
// Update User
//
// This will update user info
//
// Responses:
// 200: UpdateUserResponseWrapper
複製程式碼
這樣就把api宣告註釋給抽離出來了,然後使用命令swagger generate spec -o ./swagger.json
生成json檔案,就可以看到這樣的結果:
很簡單吧,參照文件編寫幾行註釋,然後一個命令生成API文件。懶癌程式設計師福音~
參考: