-
- 将 openapi3.0 和 swagger2.0 的支持单独列分支,master 将保持 swagger2.0 支持。解决现存的。
-
- 解决现存 openapi3.0 支持 bug。
-
- 拉新分支用https://github.com/wI2L/fizz 来适配 knife4j 的 ui,不用再生成 swagger.json。
优化 gin-swagger 的界面,让 knif4j 适配 go 语言。感谢萧明。 knif4j 地址:https://gitee.com/xiaoym/knife4j
| 模块 | 描述 |
|---|---|
| gin-swagger-knife | 替换 gin-swagger 的界面实现(并非在原项目代码上修改),供 gin 项目引用 (适用于 swagger2) |
| gin-openapi3-knife | 替换 gin-swagger 的界面实现(并非在原项目代码上修改),供 gin 项目引用 (适用于 openapi3) |
| fizz-knife | 替换 fizz 的界面实现(并非在原项目代码上修改),供 gin 项目引用 (基于 fizz 展示 openapi3,不需要用 swagger.json) ,使用样例参考源码下的 example,还可参考:wI2L/fizz: Gin wrapper with OpenAPI 3 spec generation (github.com) 项目说明,暂时没时间整理详细文档 |
| knife-vue-dist | knife4j-vue 项目打包后的 dist 目录内容,用于 knife-vue-2-go-code 中生成代码的逻辑 |
| knife-vue-2-go-code | 用于生成 gin-swagger-knife 中的前端文件接口代码(knife 目录下代码),方便根据最新版本的 knif4j 升级界面 |
| knife-openapi3-go-code | 用于生成 gin-openapi3-knife 中的前端文件接口代码(knife 目录下代码),方便根据最新版本的 knif4j 升级界面 |
| gin-swagger-knife-example | gin-swagger-knife 使用示例 |
| gin-openapi3-knife-example | gin-openapi3-knife 使用示例 |
| fizz-knife-example | fizz-knife 使用示例 |
-
- 按照传统 gin-swagger 方式生成 swagger.json,将内容拷贝到一个字符串中。关于 gin-swagger 的使用参考gin-swagger 生成 API 文档 或 swaggo 项目
- 1.1 先下载 cmd 包,才能执行相关命令 go get -u github.com/swaggo/swag/cmd/swag 我开始没成功,后来进入$GOPATH/bin/ 目录执行go get github.com/swaggo/swag/cmd/swag ,在bin目录下生成一个swag.exe文件,把$GOPATH/bin/ 添加到 Path 环境变量才算成功
- 1.2 执行初始化命令(相关的 swagger 注释写法参照 gin-swagger 官网) swag init // 注意,一定要和 main.go 处于同一级目录 初始化命令,在根目录生成一个 docs 文件夹,swagger.json 就在该文件夹中 docs/swagger.json
- 1.3 将 swagger.json 的内容拷贝到下面示例代码的 swaggerJson 变量中
-
- 引入 gin-swagger-knife 项目 go get github.com/mingcode100/knife4go/gin-swagger-knife
-
import ( "fmt" gin_swagger_knife "github.com/mingcode100/knife4go/gin-swagger-knife" "github.com/gin-gonic/gin" ) router := gin.Default() // 获取 swag int 命令生成的swagger.json文件里的内容 swaggerJson := "swagger.json的内容" //初始化gin路径 gin_swagger_knife.InitSwaggerKnife(router,swaggerJson) router.Run(":8080")
package main
import (
"fmt"
fizz_knife "github.com/mingcode100/knife4go/fizz-knife"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/juju/errors"
"github.com/loopfz/gadgeto/tonic"
"github.com/wI2L/fizz"
"github.com/wI2L/fizz/openapi"
"log"
"net/http"
"strconv"
"time"
)
// Fruit represents a sweet, fresh fruit.
type Fruit struct {
Name string `json:"name" validate:"required" description:"名称" example:"banana"`
Origin string `json:"origin" validate:"required" description:"水果的出产国家" enum:"ecuador,france,senegal,china,spain"`
Price float64 `json:"price" validate:"required" description:"价格" example:"5.13"`
AddedAt time.Time `json:"-" binding:"-" description:"Date of addition of the fruit to the market"`
}
// TypeName implements openapi.Typer interface for Fruit.
func (f *Fruit) TypeName() string { return "RottenFruit" }
func main() {
router, err := NewRouter()
if err != nil {
log.Fatal(err)
}
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
srv.ListenAndServe()
}
// NewRouter returns a new router for the
// Pet Store.
func NewRouter() (*fizz.Fizz, error) {
engine := gin.New()
engine.Use(cors.Default())
// Override type names.
// fizz.Generator().OverrideTypeName(reflect.TypeOf(Fruit{}), "SweetFruit")
// Initialize the informations of
// the API that will be served with
// the specification.
infos := &openapi.Info{
Title: "Fruits Market水果超市",
Description: `This is a sample Fruits market server. 水果超市接口`,
Version: "1.0.0",
}
fizz := fizz_knife.InitSwaggerKnife(engine, infos)
// Setup routes.
routes(fizz.Group("/market", "超市接口", "Your daily dose of freshness,你的描述"))
if len(fizz.Errors()) != 0 {
return nil, fmt.Errorf("fizz errors: %v", fizz.Errors())
}
return fizz, nil
}
func routes(grp *fizz.RouterGroup) {
// Add a new fruit to the market.
grp.POST("", []fizz.OperationOption{
fizz.Summary("给市场添加一个水果"),
fizz.Response("400", "Bad request", nil, nil,
map[string]interface{}{"error": "fruit already exists"},
),
}, tonic.Handler(CreateFruit, 200))
// Remove a fruit from the market,
// probably because it rotted.
grp.DELETE("/:name", []fizz.OperationOption{
fizz.Summary("从市场删除一个水果"),
fizz.ResponseWithExamples("400", "Bad request", nil, nil, map[string]interface{}{
"fruitNotFound": map[string]interface{}{"error": "fruit not found"},
"invalidApiKey": map[string]interface{}{"error": "invalid api key"},
}),
}, tonic.Handler(DeleteFruit, 204))
// List all available fruits.
grp.GET("", []fizz.OperationOption{
fizz.Summary("水果列表"),
fizz.Response("400", "Bad request", nil, nil, nil),
fizz.Header("X-Market-Listing-Size", "Listing size", fizz.Long),
}, tonic.Handler(ListFruits, 200))
}
// FruitIdentityParams represents the parameters that
// are required to identity a unique fruit in the market.
type FruitIdentityParams struct {
Name string `path:"name"`
APIKey string `header:"X-Api-Key" validate:"required"`
}
// ListFruitsParams represents the parameters that can
// be used to filter the fruit's market listing.
type ListFruitsParams struct {
Origin *string `query:"origin" description:"filter by fruit origin"`
PriceMin *float64 `query:"price_min" description:"filter by minimum inclusive price" validate:"omitempty,min=1"`
PriceMax *float64 `query:"price_max" description:"filter by maximum inclusive price" validate:"omitempty,max=15"`
}
// CreateFruit add a new fruit to the market.
func CreateFruit(c *gin.Context, fruit *Fruit) (*Fruit, error) {
return fruit, nil
}
// DeleteFruit removes a fruit from the market.
func DeleteFruit(c *gin.Context, params *FruitIdentityParams) error {
if params.APIKey == "" {
return errors.Forbiddenf("invalid api key")
}
return nil
}
// ListFruits lists the fruits of the market.
// Parameters can be used to filter the fruits.
func ListFruits(c *gin.Context, params *ListFruitsParams) ([]*Fruit, error) {
basket := make([]*Fruit, 0)
c.Header("X-Market-Listing-Size", strconv.Itoa(len(basket)))
return basket, nil
}

