目錄
一、安裝與配置
- 下載與安裝
- 添加環境變量
二、開始第一個 swagger 案例
- swagger 初始化
- 聲明 API 接口
- 生成接口代碼
- 添加新的功能接口
- 代碼完善
一、安裝與配置
1.下載與安裝
附件中下載 swagger 執行包,或者從 go-swaager 官網下載,根據 OS 選擇合適的版本:
這里我們下載Windows 64版本的 swagger_windows_amd64.exe
2. 添加環境變量
將 swagger_windows_amd64.exe
重命名成 swagger.exe
, 然后將該軟件放到 Go 安裝的根目錄的 bin下 $GOROOT/bin
二、開始第一個 Swagger 案例
1. swagger 初始化
新建一個 swagger_uac
空文件夾,進入文件夾中(PS:以下命令都在 Git
中運行,Git 配置):
$ mkdir swagger_uac
$ cd swagger_uac
在 Git 中輸入以下命令:
$ swagger init spec \
> --title "User Alarm Center" \
> --description "A Swagger Example" \
> --version 1.0.0 \
> --scheme http \
> --consumes application/io.goswagger.examples.uac.v1+json \
> --produces application/io.goswagger.examples.uac.v1+json
初始化成功:
2020/12/17 15:05:16 creating specification document in D:\runtime\0others\swagger_uac\swagger.yml
此時, swagger_uac
文件夾中自動生成了一個 swagger.yml
文件:
consumes:
- application/io.goswagger.examples.uac.v1+json
info:
description: A Swagger Example
title: User Alarm Center
version: 1.0.0
paths: {}
produces:
- application/io.goswagger.examples.uac.v1+json
schemes:
- http
swagger: "2.0"
不管是手動修改,還是自動生成的 yam 文件,都需要用 $ swagger validate ./swagger.yml
檢查一下是否規范:
$ swagger validate ./swagger.yml
2020/12/17 15:13:14
The swagger spec at "./swagger.yml" is valid against swagger specification 2.0
2. 聲明 API 接口
上面我們已經生成了一個框架 yam 文件,現在向文件中加入結構體對象信息:
---
definitions:
item:
type: object
required:
- description
properties:
id:
type: integer
format: int64
readOnly: true
description:
type: string
minLength: 1
completed:
type: boolean
該 item
中定義了 3 個字段:id,description,completed。上面對它們的數據類型和字段限制進行了基本定義:readOnly 是否只讀,minLength 最小長度。
然后,我們再向 yam 文件中加入 API 接口信息:
---
paths:
/:
get:
tags:
- alarms
parameters:
- name: since
in: query
type: integer
format: int64
- name: limit
in: query
type: integer
format: int32
default: 20
responses:
200:
description: list of alarm operations
schema:
type: array
items:
$ref: "#/definitions/item"
UAC 是告警模塊,所以我們定義一個 GET/
方法的 API 接口,標簽為 alarms
。入參 since
為告警查詢的最小 ID 值, limit
限制查詢的條數,默認一次查詢 20 條。響應碼定義為 200,返回體是一個 array,這個 array 引用了我們剛才定義的結構體對象 $ref: "#/definitions/item"
。
為了實現接口的最小功能集,我們還應該捕獲接口的錯誤返回。接下來,我們再定義一個 error
結構體對象,對象中有錯誤碼和錯誤信息兩個字段:
---
definitions:
error:
type: object
required:
- message
properties:
code:
type: integer
format: int64
message:
type: string
修改接口的 yam
定義,當響應碼不為 200 時,返回 error
對象:
---
paths:
/:
get:
tags:
- alarms
parameters:
- name: limit
in: query
type: integer
format: int32
default: 20
responses:
200:
description: list of alarm operations
schema:
type: array
items:
$ref: "#/definitions/item"
default:
description: generic error response
schema:
$ref: "#/definitions/error"
此時,swagger.yam 中的所有代碼為:
consumes:
- application/io.goswagger.examples.uac.v1+json
info:
description: A Swagger Example
title: User Alarm Center
version: 1.0.0
paths: {}
produces:
- application/io.goswagger.examples.uac.v1+json
schemes:
- http
swagger: "2.0"
paths:
/:
get:
tags:
- alarms
parameters:
- name: since
in: query
type: integer
format: int64
- name: limit
in: query
type: integer
format: int32
default: 20
responses:
200:
description: list of alarm operations
schema:
type: array
items:
$ref: "#/definitions/item"
default:
description: generic error response
schema:
$ref: "#/definitions/error"
definitions:
item:
type: object
required:
- description
properties:
id:
type: integer
format: int64
readOnly: true
description:
type: string
minLength: 1
completed:
type: boolean
error:
type: object
required:
- message
properties:
code:
type: integer
format: int64
message:
type: string
再次校驗:
$ swagger validate ./swagger.yml
2020/12/17 16:11:16
The swagger spec at "./swagger.yml" is valid against swagger specification 2.0
3. 生成接口代碼
先引入 gomod 模塊依賴管理工具, 然后通過 yam 文件生成框架代碼:
$ go mod init uac
go: creating new go.mod: module uac
$ swagger generate server -A uac -f ./swagger.yml
2020/12/17 17:06:45 validating spec D:\runtime\0others\swagger_uac\swagger.yml
2020/12/17 17:06:45 preprocessing spec with option: minimal flattening
2020/12/17 17:06:45 building a plan for generation
2020/12/17 17:06:45 generation target ./
...
2020/12/17 17:06:45 Generation completed!
For this generation to compile you need to have some packages in your GOPATH:
* github.com/go-openapi/runtime
* github.com/jessevdk/go-flags
You can get these now with: go get -u -f ./...
取文末的附件安裝包,或從官網下載。安裝 tree,將安裝文件夾 /bin/ 目錄下的 tree.exe
復制到 Git 安裝的 usr 目錄的 bin下:
重新打開 Git,查看 tree 是否安裝成功:
$ tree --version
tree v1.5.2.2 (c) 1996 - 2009 by Steve Baker, Thomas Moore, Francesc Rocher, Kyosuke Tokoro
查看當前的目錄結構:
$ tree
.
|-- cmd
| `-- uac-server
| `-- main.go
|-- go.mod
|-- models
| |-- error.go
| `-- item.go
|-- restapi
| |-- configure_uac.go
| |-- doc.go
| |-- embedded_spec.go
| |-- operations
| | |-- alarms
| | | |-- get.go
| | | |-- get_parameters.go
| | | |-- get_responses.go
| | | `-- get_urlbuilder.go
| | `-- uac_api.go
| `-- server.go
`-- swagger.yml
6 directories, 14 files
一個個文件夾看:
cmd/uac-server:文件夾名稱根據上面命令的 -A 后的參數 uac 加上 server 生成
models:yam 文件中定義的結構體對象
restapi:根據 swagger 規范中的 paths 屬性生成,tags 將操作分組到包中
此時可以啟動服務器,首先安裝二進制文件:
$ go install ./cmd/uac-server/
go: finding module for package github.com/jessevdk/go-flags
go: finding module for package github.com/go-openapi/loads
...
運行前, 我們先用 help
命令看一下這個服務的功能:
$ uac-server --help
Usage:
C:\Users\y30002195\go\bin\uac-server.exe [OPTIONS]
A Swagger Example
Application Options:
/scheme: the listeners to enable, this can be repeated and
defaults to the schemes in the swagger spec
/cleanup-timeout: grace period for which to wait before killing idle
connections (default: 10s)
/graceful-timeout: grace period for which to wait before shutting down
the server (default: 15s)
/max-header-size: controls the maximum number of bytes the server will
read parsing the request header's keys and values,
including the request line. It does not limit the
size of the request body. (default: 1MiB)
/socket-path: the unix socket to listen on (default:
/var/run/uac.sock)
/host: the IP to listen on (default: localhost) [%HOST%]
/port: the port to listen on for insecure connections,
defaults to a random value [%PORT%]
/listen-limit: limit the number of outstanding requests
/keep-alive: sets the TCP keep-alive timeouts on accepted
connections. It prunes dead TCP connections ( e.g.
closing laptop mid-download) (default: 3m)
/read-timeout: maximum duration before timing out read of the
request (default: 30s)
/write-timeout: maximum duration before timing out write of the
response (default: 60s)
/tls-host: the IP to listen on for tls, when not specified it's
the same as --host [%TLS_HOST%]
/tls-port: the port to listen on for secure connections,
defaults to a random value [%TLS_PORT%]
/tls-certificate: the certificate to use for secure connections
[%TLS_CERTIFICATE%]
/tls-key: the private key to use for secure connections
[%TLS_PRIVATE_KEY%]
/tls-ca: the certificate authority file to be used with
mutual tls auth [%TLS_CA_CERTIFICATE%]
/tls-listen-limit: limit the number of outstanding requests
/tls-keep-alive: sets the TCP keep-alive timeouts on accepted
connections. It prunes dead TCP connections ( e.g.
closing laptop mid-download)
/tls-read-timeout: maximum duration before timing out read of the
request
/tls-write-timeout: maximum duration before timing out write of the
response
Help Options:
/? Show this help message
/h, /help Show this help message
如果我們現在運行應用程序,默認情況下,它將在隨機端口啟動。我們也可以通過命令行參數或 port env
變量配置端口(見下文)。
$ uac-server
2020/12/17 17:53:36 Serving uac at http://127.0.0.1:57294
另起一個 Git,或者使用接口調用工具訪問服務地址:
$ curl -i http://127.0.0.1:53472
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 52 100 52 0 0 52000 0 --:--:-- --:--:-- --:--:-- 52000HTTP/1.1 501 Not Implemented
Content-Type: application/io.goswagger.examples.uac.v1+json
Date: Thu, 17 Dec 2020 11:06:36 GMT
Content-Length: 52
"operation alarms.Get has not yet been implemented"
4. 添加新的功能接口
正如你看到的那樣,調用該接口會返回 501 (接口未實現)。為了讓當前 API 實現真正的可用性,我們需要從邏輯上實現它。與此同時,我們還可以加一個告警新增的功能 post
:
---
paths:
/:
post:
tags:
- alarms
operationId: addAlarm
parameters:
- name: body
in: body
schema:
$ref: "#/definitions/item"
responses:
201:
description: Created
schema:
$ref: "#/definitions/item"
default:
description: error
schema:
$ref: "#/definitions/error"
注意,我們新增了 operationId
屬性,它可以在 tags 包下面生成帶屬性名稱的 Go 文件。
接下來,我們再定義一個刪除的功能:
---
paths:
/{id}:
delete:
tags:
- alarms
operationId: deleteAlarm
parameters:
- type: integer
format: int64
name: id
in: path
required: true
responses:
204:
description: Deleted
default:
description: error
schema:
$ref: "#/definitions/error"
最后,定義修改的功能(由於/{id} 路徑是 DELETE 和 PUT 共享的,因此它們可以共享一個參數定義):
---
paths:
/{id}:
parameters:
- type: integer
format: int64
name: id
in: path
required: true
put:
tags:
- alarms
operationId: updateAlarm
parameters:
- name: body
in: body
schema:
$ref: "#/definitions/item"
responses:
'200':
description: OK
schema:
$ref: "#/definitions/item"
default:
description: error
schema:
$ref: "#/definitions/error"
delete:
# elided for brevity
再來看看完整的 API 規范(注意縮進不要弄錯):
consumes:
- application/io.goswagger.examples.uac.v1+json
info:
description: A Swagger Example
title: User Alarm Center
version: 1.0.0
paths: {}
produces:
- application/io.goswagger.examples.uac.v1+json
schemes:
- http
- https
swagger: "2.0"
paths:
/:
get:
tags:
- alarms
operationId: getAlarms
parameters:
- name: since
in: query
type: integer
format: int64
- name: limit
in: query
type: integer
format: int32
default: 20
responses:
200:
description: list of alarm operations
schema:
type: array
items:
$ref: "#/definitions/item"
default:
description: generic error response
schema:
$ref: "#/definitions/error"
post:
tags:
- alarms
operationId: addAlarm
parameters:
- name: body
in: body
schema:
$ref: "#/definitions/item"
responses:
201:
description: Created
schema:
$ref: "#/definitions/item"
default:
description: error
schema:
$ref: "#/definitions/error"
/{id}:
parameters:
- type: integer
format: int64
name: id
in: path
required: true
put:
tags:
- alarms
operationId: updateAlarm
parameters:
- name: body
in: body
schema:
$ref: "#/definitions/item"
responses:
'200':
description: OK
schema:
$ref: "#/definitions/item"
default:
description: error
schema:
$ref: "#/definitions/error"
delete:
tags:
- alarms
operationId: deleteAlarm
parameters:
- type: integer
format: int64
name: id
in: path
required: true
responses:
204:
description: Deleted
default:
description: error
schema:
$ref: "#/definitions/error"
definitions:
item:
type: object
required:
- description
properties:
id:
type: integer
format: int64
readOnly: true
description:
type: string
minLength: 1
completed:
type: boolean
error:
type: object
required:
- message
properties:
code:
type: integer
format: int64
message:
type: string
swagger.yam
文件校驗:
$ swagger validate ./swagger.yml
2020/12/17 21:35:42
The swagger spec at "./swagger.yml" is valid against swagger specification 2.0
再次生成 API 代碼文件:
$ swagger generate server -A uac -f ./swagger.yml
2020/12/17 21:37:14 validating spec D:\runtime\0others\swagger_uac\swagger.yml
2020/12/17 21:37:14 preprocessing spec with option: minimal flattening
...
2020/12/17 21:37:14 Generation completed!
For this generation to compile you need to have some packages in your GOPATH:
* github.com/go-openapi/runtime
* github.com/jessevdk/go-flags
You can get these now with: go get -u -f ./...
查看文件目錄:
$ tree
.
|-- cmd
| `-- uac-server
| `-- main.go
|-- go.mod
|-- go.sum
|-- models
| |-- error.go
| `-- item.go
|-- restapi
| |-- configure_uac.go
| |-- doc.go
| |-- embedded_spec.go
| |-- operations
| | |-- alarms
| | | |-- add_alarm.go
| | | |-- add_alarm_parameters.go
| | | |-- add_alarm_responses.go
| | | |-- add_alarm_urlbuilder.go
| | | |-- delete_alarm.go
| | | |-- delete_alarm_parameters.go
| | | |-- delete_alarm_responses.go
| | | |-- delete_alarm_urlbuilder.go
| | | |-- get.go
| | | |-- get_alarms.go
| | | |-- get_alarms_parameters.go
| | | |-- get_alarms_responses.go
| | | |-- get_alarms_urlbuilder.go
| | | |-- get_parameters.go
| | | |-- get_responses.go
| | | |-- get_urlbuilder.go
| | | |-- update_alarm.go
| | | |-- update_alarm_parameters.go
| | | |-- update_alarm_responses.go
| | | `-- update_alarm_urlbuilder.go
| | `-- uac_api.go
| `-- server.go
`-- swagger.yml
6 directories, 31 files
5. 代碼完善
新文件生成了,但我們還需要做一點改動,打開 restapi/configure_todo_list.go
文件。首先,在第一個函數前新增兩個全局變量:
// API 全程需要的變量
var items = make(map[int64]*models.Item)
var lastID int64
先在 configureAPI
函數中實現最簡單的 delete 方法:
api.AlarmsDeleteAlarmHandler = alarms.DeleteAlarmHandlerFunc(func(params alarms.DeleteAlarmParams) middleware.Responder {
delete(items, params.ID)
return alarms.NewDeleteAlarmNoContent()
})
如上所示:我們雖然修改了查詢告警
getAlarm
接口的規范,但是舊的代碼不會被覆蓋,所以我們先把之前的代碼刪除掉,並添加增、改、查的代碼。
新增的全部代碼:
// API 全程需要的變量
var items = make(map[int64]*models.Item)
var lastID int64
var itemsLock = &sync.Mutex{}
func newItemID() int64 {
return atomic.AddInt64(&lastID, 1)
}
func addItem(item *models.Item) error {
if item == nil {
return errors.New(500, "item must be present")
}
itemsLock.Lock()
defer itemsLock.Unlock()
newID := newItemID()
item.ID = newID
items[newID] = item
return nil
}
func updateItem(id int64, item *models.Item) error {
if item == nil {
return errors.New(500, "item must be present")
}
itemsLock.Lock()
defer itemsLock.Unlock()
_, exists := items[id]
if !exists {
return errors.NotFound("not found: item %d", id)
}
item.ID = id
items[id] = item
return nil
}
func deleteItem(id int64) error {
itemsLock.Lock()
defer itemsLock.Unlock()
_, exists := items[id]
if !exists {
return errors.NotFound("not found: item %d", id)
}
delete(items, id)
return nil
}
func allItems(since int64, limit int32) (result []*models.Item) {
result = make([]*models.Item, 0)
for id, item := range items {
if len(result) >= int(limit) {
return
}
if since == 0 || id > since {
result = append(result, item)
}
}
return
}
func configureAPI(api *operations.UacAPI) http.Handler {
...
api.AlarmsAddAlarmHandler = alarms.AddAlarmHandlerFunc(func(params alarms.AddAlarmParams) middleware.Responder {
if err := addItem(params.Body); err != nil {
return alarms.NewAddAlarmDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
return alarms.NewAddAlarmCreated().WithPayload(params.Body)
})
api.AlarmsDeleteAlarmHandler = alarms.DeleteAlarmHandlerFunc(func(params alarms.DeleteAlarmParams) middleware.Responder {
if err := deleteItem(params.ID); err != nil {
return alarms.NewDeleteAlarmDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
return alarms.NewDeleteAlarmNoContent()
})
api.AlarmsUpdateAlarmHandler = alarms.UpdateAlarmHandlerFunc(func(params alarms.UpdateAlarmParams) middleware.Responder {
if err := updateItem(params.ID, params.Body); err != nil {
return alarms.NewUpdateAlarmDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
return alarms.NewUpdateAlarmOK().WithPayload(params.Body)
})
api.AlarmsGetAlarmsHandler = alarms.GetAlarmsHandlerFunc(func(params alarms.GetAlarmsParams) middleware.Responder {
mergedParams := alarms.NewGetAlarmsParams()
mergedParams.Since = swag.Int64(0)
if params.Since != nil {
mergedParams.Since = params.Since
}
if params.Limit != nil {
mergedParams.Limit = params.Limit
}
return alarms.NewGetAlarmsOK().WithPayload(allItems(*mergedParams.Since, *mergedParams.Limit))
})
...
}
再次安裝,運行服務:
$ go install ./cmd/uac-server/
$ uac-server --port 63192
2020/12/17 22:25:00 Serving uac at http://127.0.0.1:63192
調用接口,查詢成功:
$ curl -i http://127.0.0.1:63192
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3 100 3 0 0 3000 0 --:--:-- --:--:-- --:--:-- 3000HTTP/1.1 200 OK
Content-Type: application/io.goswagger.examples.uac.v1+json
Date: Thu, 17 Dec 2020 14:25:30 GMT
Content-Length: 3
[]
向 uac 中連續添加 3 條告警信息,注意 json 格式為 application/io.goswagger.examples.uac.v1+json
$ curl -i http://127.0.0.1:63192 -d "{\"description\":\"message $RANDOM\"}" -H 'Content-Type: application/io.goswagger.examples.uac.v1+json'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 68 100 38 100 30 38000 30000 --:--:-- --:--:-- --:--:-- 68000HTTP/1.1 201 Created
Content-Type: application/io.goswagger.examples.uac.v1+json
Date: Fri, 18 Dec 2020 01:31:23 GMT
Content-Length: 38
{"description":"message 9934","id":1}
// 繼續調用兩次添加命令
查詢:
$ curl -i localhost:63192
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 118 100 118 0 0 18 0 0:00:06 0:00:06 --:--:-- 27HTTP/1.1 200 OK
Content-Type: application/io.goswagger.examples.uac.v1+json
Date: Fri, 18 Dec 2020 01:33:08 GMT
Content-Length: 118
[{"description":"message 9934","id":1},{"description":"message 19901","id":2},{"description":"message 16380","id":3}]
刪除 ID 為 1 的告警消息:
$ curl -i localhost:63192/1 -X DELETE
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0HTTP/1.1 204 No Content
Date: Fri, 18 Dec 2020 01:37:31 GMT
查詢:
$ curl -i localhost:63192
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 80 100 80 0 0 12 0 0:00:06 0:00:06 --:--:-- 18HTTP/1.1 200 OK
Content-Type: application/io.goswagger.examples.uac.v1+json
Date: Fri, 18 Dec 2020 01:37:49 GMT
Content-Length: 80
[{"description":"message 19901","id":2},{"description":"message 16380","id":3}]
修改 ID 為 3 的 message
信息:
$ curl -i localhost:63192/3 -d "{\"description\":\"message has been updated\"}" -X PUT -H 'Content-Type: application/io.goswagger.examples.uac.v1+json'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 92 100 50 100 42 3125 2625 --:--:-- --:--:-- --:--:-- 5750HTTP/1.1 200 OK
Content-Type: application/io.goswagger.examples.uac.v1+json
Date: Fri, 18 Dec 2020 01:49:35 GMT
Content-Length: 50
{"description":"message has been updated","id":3}
PS:除了用命令的形式,也可用 postman 或 insomnia 調用接口: