如何从Go源代码生成OpenAPI v3规范?

mat*_*usf 17 go openapi

有没有办法从Go源代码生成OpenAPI v3规范?假设我有一个如下所示的 go API,我想从中生成 OpenAPI 规范(yaml 文件)。类似于 Python 的Flask RESTX。我知道有一些工具可以根据规范生成 go 源代码,但是,我想以相反的方式进行。

package main

import "net/http"

func main() {
    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("world\n"))
    })
    http.ListenAndServe(":5050", nil)
}
Run Code Online (Sandbox Code Playgroud)

vea*_*top 12

您可以使用github.com/swaggest/rest构建自记录 HTTP REST API。该库建立了一个约定,以可用于反映文档和模式并维护有关它的单一事实来源的方式声明处理程序。

\n

在我个人看来,与规范优先方法相比,代码优先方法具有优势。它不需要成为规范语言语法方面的专家,因此可以降低入门门槛。它可能有助于提出一个与实现细节很好平衡的规范。

\n

使用代码优先方法,无需实现完整的服务来获取规范。您只需要定义结构和接口,可能会推迟实际的逻辑实现。

\n

请查看一个简短的使用示例

\n
package main\n\nimport (\n    "context"\n    "errors"\n    "fmt"\n    "log"\n    "net/http"\n    "time"\n\n    "github.com/go-chi/chi"\n    "github.com/go-chi/chi/middleware"\n    "github.com/swaggest/rest"\n    "github.com/swaggest/rest/chirouter"\n    "github.com/swaggest/rest/jsonschema"\n    "github.com/swaggest/rest/nethttp"\n    "github.com/swaggest/rest/openapi"\n    "github.com/swaggest/rest/request"\n    "github.com/swaggest/rest/response"\n    "github.com/swaggest/rest/response/gzip"\n    "github.com/swaggest/swgui/v3cdn"\n    "github.com/swaggest/usecase"\n    "github.com/swaggest/usecase/status"\n)\n\nfunc main() {\n    // Init API documentation schema.\n    apiSchema := &openapi.Collector{}\n    apiSchema.Reflector().SpecEns().Info.Title = "Basic Example"\n    apiSchema.Reflector().SpecEns().Info.WithDescription("This app showcases a trivial REST API.")\n    apiSchema.Reflector().SpecEns().Info.Version = "v1.2.3"\n\n    // Setup request decoder and validator.\n    validatorFactory := jsonschema.NewFactory(apiSchema, apiSchema)\n    decoderFactory := request.NewDecoderFactory()\n    decoderFactory.ApplyDefaults = true\n    decoderFactory.SetDecoderFunc(rest.ParamInPath, chirouter.PathToURLValues)\n\n    // Create router.\n    r := chirouter.NewWrapper(chi.NewRouter())\n\n    // Setup middlewares.\n    r.Use(\n        middleware.Recoverer,                          // Panic recovery.\n        nethttp.OpenAPIMiddleware(apiSchema),          // Documentation collector.\n        request.DecoderMiddleware(decoderFactory),     // Request decoder setup.\n        request.ValidatorMiddleware(validatorFactory), // Request validator setup.\n        response.EncoderMiddleware,                    // Response encoder setup.\n        gzip.Middleware,                               // Response compression with support for direct gzip pass through.\n    )\n\n    // Create use case interactor.\n    u := usecase.IOInteractor{}\n\n    // Describe use case interactor.\n    u.SetTitle("Greeter")\n    u.SetDescription("Greeter greets you.")\n\n    // Declare input port type.\n    type helloInput struct {\n        Locale string `query:"locale" default:"en-US" pattern:"^[a-z]{2}-[A-Z]{2}$" enum:"ru-RU,en-US"`\n        Name   string `path:"name" minLength:"3"` // Field tags define parameter location and JSON schema constraints.\n    }\n    u.Input = new(helloInput)\n\n    // Declare output port type.\n    type helloOutput struct {\n        Now     time.Time `header:"X-Now" json:"-"`\n        Message string    `json:"message"`\n    }\n    u.Output = new(helloOutput)\n\n    u.SetExpectedErrors(status.InvalidArgument)\n    messages := map[string]string{\n        "en-US": "Hello, %s!",\n        "ru-RU": "\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, %s!",\n    }\n    u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error {\n        var (\n            in  = input.(*helloInput)\n            out = output.(*helloOutput)\n        )\n\n        msg, available := messages[in.Locale]\n        if !available {\n            return status.Wrap(errors.New("unknown locale"), status.InvalidArgument)\n        }\n\n        out.Message = fmt.Sprintf(msg, in.Name)\n        out.Now = time.Now()\n\n        return nil\n    })\n\n    // Add use case handler to router.\n    r.Method(http.MethodGet, "/hello/{name}", nethttp.NewHandler(u))\n\n    // Swagger UI endpoint at /docs.\n    r.Method(http.MethodGet, "/docs/openapi.json", apiSchema)\n    r.Mount("/docs", v3cdn.NewHandler(apiSchema.Reflector().Spec.Info.Title,\n        "/docs/openapi.json", "/docs"))\n\n    // Start server.\n    log.Println("http://localhost:8011/docs")\n    if err := http.ListenAndServe(":8011", r); err != nil {\n        log.Fatal(err)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Dan*_*iel 5

您可以使用Huma(完全公开:我是作者)从 Go 代码生成 OpenAPI 3.1 和 JSON Schema,它可以与各种流行的路由器配合使用,因此可以根据需要集成到现有的代码库中。Huma 利用 Go 泛型为输入参数、主体、响应头等提供编译时检查。

这是一个简单的 hello world 示例,只有几行:

package main

import (
    "context"
    "fmt"
    "net/http"

    "github.com/danielgtaylor/huma/v2"
    "github.com/danielgtaylor/huma/v2/adapters/humachi"
    "github.com/go-chi/chi/v5"
)

// GreetingInput represents the greeting operation request.
type GreetingInput struct {
    Name string `path:"name" maxLength:"30" example:"world" doc:"Name to greet"`
}

// GreetingOutput represents the greeting operation response.
type GreetingOutput struct {
    Body struct {
        Message string `json:"message" example:"Hello, world!" doc:"Greeting message"`
    }
}

func main() {
    // Create a new router & API
    router := chi.NewMux()
    api := humachi.New(router, huma.DefaultConfig("My API", "1.0.0"))

    // Register GET /greeting/{name}
    huma.Register(api, huma.Operation{
        OperationID: "get-greeting",
        Summary:     "Get a greeting",
        Method:      http.MethodGet,
        Path:        "/greeting/{name}",
    }, func(ctx context.Context, input *GreetingInput) (*GreetingOutput, error) {
        resp := &GreetingOutput{}
        resp.Body.Message = fmt.Sprintf("Hello, %s!", input.Name)
        return resp, nil
    })

    // Start the server!
    http.ListenAndServe("127.0.0.1:8888", router)
}
Run Code Online (Sandbox Code Playgroud)

然后您可以轻松地从服务中获取生成的 OpenAPI:

curl http://localhost:8888/openapi.json
Run Code Online (Sandbox Code Playgroud)

您还可以在 处查看从 OpenAPI 生成的文档http://localhost:8888/docs

  • 多漂亮的图书馆啊,我一定要去看看,谢谢! (2认同)
  • 辉煌的图书馆 (2认同)