[筆記] oapi gencode 基礎使法
Golang Swagger3.0 方案之一
- 為Swagger 3.0 config first的 Golang codegen工具, oapi-codegen
- 主要是藉由swagger config生成API的輸入輸出類型相關的程式碼(request參數輸入,response輸出的 marshal/unmarshal)
優點為
- 可以保持API文件與程式碼的一致性
- 節省mashalling/unmashalling的時間
- 只需要開發者專注於handler的實現
支援
- Chi
- Echo
- Fiber
- Gin
- gorilla/mux
- Iris
- net/http (Go 1.22+)
差異
之前用過gin-swagger這類的code first工具來產生swagger文件,
除了只支援swagger2.0之外, 藉由撰寫註釋的方式來生成swagger文檔維護上沒有想像中的好用,所有的異動都需要靠自己來維護
唯一的優點是比較直覺一些,對於初次使用者來說比較容易上手
安裝
go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest
oapi-codegen -version
gencode 指令
oapi-codegen --package=<package name> <swagger config yaml/json>
## 之後會返回生成的程式碼
實際範例
oapi-codegen --package=api dist/openapi_test.yaml > api/gencode.go
開發流程
- 編寫openapi文件
- 產生程式碼
- 實作API的handler (request input/response output類型可直接調用上面產生的程式碼)
- 實做server端的main函數, 將handler與路由綁定, 啟動server
使用範例1
這邊是使用 oapi-codegen的範例, 實做一個最簡單的API Demo
oapi-gencode 編寫的swagger文件需依賴Swagger的編輯器才能瀏覽效果, 這邊會新增一個swagger的WebUI來方便瀏覽效果
初始化
go mod init mininal_echo
go mod tidy
mkdir api
## 也是之後gencode的package name, 之後會將生成的程式碼放在這個目錄下
需先去swagger clone下來, 並將dist目錄複製到專案目錄下
openapi文件
編寫新增 dist/openapi_test.yaml
openapi: "3.0.0"
info:
version: 1.0.0
title: Minimal ping API server
paths:
/ping:
get:
responses:
'200':
description: pet response
content:
application/json:
schema:
$ref: '#/components/schemas/Pong'
tags:
- PingDemo
components:
schemas:
# base types
Pong:
type: object
required:
- ping
properties:
ping:
type: string
example: pong
oapi-codegen --package=api dist/openapi_test.yaml > api/gencode.go
修改swagger dist config目標
需把swagger dist套用的設定檔改成剛剛寫的openapi_test.yaml
dist/swagger-initializer.js
window.onload = function() {
//<editor-fold desc="Changeable Configuration Block">
// the following lines will be replaced by docker/configurator, when it runs in a docker-container
window.ui = SwaggerUIBundle({
url: "./openapi_test.yaml", //這邊改成剛設定的swagger config
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
// plugins: [ // 這邊是關閉swagger ui的topbar, 不影響使用
// SwaggerUIBundle.plugins.DownloadUrl
// ],
// layout: "StandaloneLayout"
});
//</editor-fold>
};
handler
藉由oapi-codegen 生成程式碼(request input,response output), 編寫新增api/impl.go
package api
import (
"net/http"
"github.com/labstack/echo/v4"
)
// ensure that we've conformed to the `ServerInterface` with a compile-time check
var _ ServerInterface = (*Server)(nil)
type Server struct{}
func NewServer() Server {
return Server{}
}
// (GET /ping)
func (Server) GetPing(ctx echo.Context) error {
resp := Pong{
Ping: "pong",
}
return ctx.JSON(http.StatusOK, resp)
}
main.go
編寫新增 ./main.go, 實作server端的main函數, 將handler與路由綁定, 啟動server
這邊用echo 來演示, 使用不同api框架, 只會在這邊的實作有所不同
package main
import (
"github.com/labstack/echo/v4"
"log"
"net/http"
"minimal_echo/api"
)
func main() {
// create a type that satisfies the `api.ServerInterface`, which contains an implementation of every operation from the generated code
server := api.NewServer()
e := echo.New()
// Serve the Swagger UI files and the dist folder
e.GET("/docs/*", echo.WrapHandler(http.StripPrefix("/docs/", http.FileServer(http.Dir("./dist")))))
api.RegisterHandlers(e, server)
// And we serve HTTP until the world ends.
log.Fatal(e.Start("0.0.0.0:8080"))
}
目錄結構
此時目錄結構如下
minimal_echo/
├── api
│ ├── gencode.go
│ └── impl.go
├── dist
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── index.css
│ ├── index.html
│ ├── oauth2-redirect.html
│ ├── openapi_test.yaml
│ ├── swagger-initializer.js
│ ├── swagger-ui-bundle.js
│ ├── swagger-ui-bundle.js.map
│ ├── swagger-ui.css
│ ├── swagger-ui.css.map
│ ├── swagger-ui-es-bundle-core.js
│ ├── swagger-ui-es-bundle-core.js.map
│ ├── swagger-ui-es-bundle.js
│ ├── swagger-ui-es-bundle.js.map
│ ├── swagger-ui.js
│ ├── swagger-ui.js.map
│ ├── swagger-ui-standalone-preset.js
│ └── swagger-ui-standalone-preset.js.map
├── go.mod
├── go.sum
└── main.go
啟動server
go build main.go
./main
此時可訪問 http://localhost:8080/docs/ 來瀏覽swagger文檔