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

如何从Go源代码生成OpenAPI v3规范?,go,openapi,Go,Openapi,有没有办法从go源代码生成OpenAPI v3规范?让我们假设我有一个尝试 API类似于下面的API,我想从中生成OpenAPI规范(yaml文件)。类似于Python的东西。我知道有一些工具可以从规范中生成go源代码,但是,我想用另一种方法 主程序包 导入“net/http” func main(){ http.HandleFunc(“/hello”,func(w http.ResponseWriter,r*http.Request){ w、 写入([]字节(“世界\n”)) }) http.

有没有办法从go源代码生成OpenAPI v3规范?让我们假设我有一个尝试 API类似于下面的API,我想从中生成OpenAPI规范(yaml文件)。类似于Python的东西。我知道有一些工具可以从规范中生成go源代码,但是,我想用另一种方法

主程序包
导入“net/http”
func main(){
http.HandleFunc(“/hello”,func(w http.ResponseWriter,r*http.Request){
w、 写入([]字节(“世界\n”))
})
http.listendServe(“:5050”,无)
}
您可以使用它来构建一个自文档化的HTTP REST API。这个库建立了一个约定,以一种可以用来反映文档和模式并维护其真实性的单一来源的方式声明处理程序

在我个人看来,代码优先的方法比规范优先的方法有优势。它可以通过不需要精通spec语言语法来降低输入栏。它可能有助于制定一个与实现细节很好地平衡的规范

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

请检查一下简短的用法


首先编写API实现,然后为其生成规范,这没有多大意义。OpenAPI的用途正好相反。同样,这样的规范也很难完全符合上述陈述(该工具如何知道中间件处理的auth头或处理程序中实现的输入限制等)。我总是先编写openapi YAML,然后再编写代码。确保所有路由的输入和输出都是一致的,特别是在使用schema
$ref
references时。OpenAPI作为规范不建议使用哪种方式创建它。这一切都涉及到特定的工具和约定。将有关安全定义的信息连接到API文档中是绝对可行的,例如,请检查。此外,从代码表达规范通常不太容易出现人为错误和过时。我坚信,使用从代码生成的OpenAPI规范作为如何实现客户端的权威通常是一个坏主意,类似于在编写应用程序后编写UI/功能设计文档。也就是说,从源代码生成OpenAPI规范绝对有其原因。这允许您将生成的规范与设计的规范进行比较,以确保它与设计兼容。首先,它必须是准确的。其次,它必须在合理的时间间隔内向后兼容。这些属性都不是spec first方法唯一授予的。从源反射的规范具有最高的可能精度。手工编写规范是一个乏味的过程,容易出现错误和不一致。相反,在Go中编写零实现受到编译时类型安全的保护,一旦导出的API规范得到同行的批准,就可以进行进一步的实际实现。
package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/go-chi/chi"
    "github.com/go-chi/chi/middleware"
    "github.com/swaggest/rest"
    "github.com/swaggest/rest/chirouter"
    "github.com/swaggest/rest/jsonschema"
    "github.com/swaggest/rest/nethttp"
    "github.com/swaggest/rest/openapi"
    "github.com/swaggest/rest/request"
    "github.com/swaggest/rest/response"
    "github.com/swaggest/rest/response/gzip"
    "github.com/swaggest/swgui/v3cdn"
    "github.com/swaggest/usecase"
    "github.com/swaggest/usecase/status"
)

func main() {
    // Init API documentation schema.
    apiSchema := &openapi.Collector{}
    apiSchema.Reflector().SpecEns().Info.Title = "Basic Example"
    apiSchema.Reflector().SpecEns().Info.WithDescription("This app showcases a trivial REST API.")
    apiSchema.Reflector().SpecEns().Info.Version = "v1.2.3"

    // Setup request decoder and validator.
    validatorFactory := jsonschema.NewFactory(apiSchema, apiSchema)
    decoderFactory := request.NewDecoderFactory()
    decoderFactory.ApplyDefaults = true
    decoderFactory.SetDecoderFunc(rest.ParamInPath, chirouter.PathToURLValues)

    // Create router.
    r := chirouter.NewWrapper(chi.NewRouter())

    // Setup middlewares.
    r.Use(
        middleware.Recoverer,                          // Panic recovery.
        nethttp.OpenAPIMiddleware(apiSchema),          // Documentation collector.
        request.DecoderMiddleware(decoderFactory),     // Request decoder setup.
        request.ValidatorMiddleware(validatorFactory), // Request validator setup.
        response.EncoderMiddleware,                    // Response encoder setup.
        gzip.Middleware,                               // Response compression with support for direct gzip pass through.
    )

    // Create use case interactor.
    u := usecase.IOInteractor{}

    // Describe use case interactor.
    u.SetTitle("Greeter")
    u.SetDescription("Greeter greets you.")

    // Declare input port type.
    type helloInput struct {
        Locale string `query:"locale" default:"en-US" pattern:"^[a-z]{2}-[A-Z]{2}$" enum:"ru-RU,en-US"`
        Name   string `path:"name" minLength:"3"` // Field tags define parameter location and JSON schema constraints.
    }
    u.Input = new(helloInput)

    // Declare output port type.
    type helloOutput struct {
        Now     time.Time `header:"X-Now" json:"-"`
        Message string    `json:"message"`
    }
    u.Output = new(helloOutput)

    u.SetExpectedErrors(status.InvalidArgument)
    messages := map[string]string{
        "en-US": "Hello, %s!",
        "ru-RU": "Привет, %s!",
    }
    u.Interactor = usecase.Interact(func(ctx context.Context, input, output interface{}) error {
        var (
            in  = input.(*helloInput)
            out = output.(*helloOutput)
        )

        msg, available := messages[in.Locale]
        if !available {
            return status.Wrap(errors.New("unknown locale"), status.InvalidArgument)
        }

        out.Message = fmt.Sprintf(msg, in.Name)
        out.Now = time.Now()

        return nil
    })

    // Add use case handler to router.
    r.Method(http.MethodGet, "/hello/{name}", nethttp.NewHandler(u))

    // Swagger UI endpoint at /docs.
    r.Method(http.MethodGet, "/docs/openapi.json", apiSchema)
    r.Mount("/docs", v3cdn.NewHandler(apiSchema.Reflector().Spec.Info.Title,
        "/docs/openapi.json", "/docs"))

    // Start server.
    log.Println("http://localhost:8011/docs")
    if err := http.ListenAndServe(":8011", r); err != nil {
        log.Fatal(err)
    }
}