swag

package module
v1.14.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2022 License: Apache-2.0 Imports: 16 Imported by: 14

README

swag

LICENSE GoDoc Go Report Card

English | 简体中文

swag is a lightweight library to generate swagger json for Golang projects.

swag is heavily geared towards generating REST/JSON apis.

No code generation, no framework constraints, just a simple swagger definition.

Dependency

Golang 1.16+

Installation

go get -u github.com/zc2638/swag

Tip: As of v1.2.0, lower versions are no longer compatible. In order to be compatible with most web frameworks, the overall architecture has been greatly changed.

Default Swagger UI Server

func main() {
    handle := swag.UIHandler("/swagger/ui", "", false)
    patterns := swag.UIPatterns("/swagger/ui")
    for _, pattern := range patterns {
        http.DefaultServeMux.Handle(pattern, handle)
    }
    
    log.Fatal(http.ListenAndServe(":8080", nil))
}

so you can visit for UI: http://localhost:8080/swagger/ui

Examples

definition
package main

import (
	"fmt"
	"io"
	"net/http"

	"github.com/zc2638/swag"
	"github.com/zc2638/swag/endpoint"
	"github.com/zc2638/swag/option"
)

// Category example from the swagger pet store
type Category struct {
	ID     int64  `json:"category"`
	Name   string `json:"name" enum:"dog,cat" required:""`
	Exists *bool  `json:"exists" required:""`
}

// Pet example from the swagger pet store
type Pet struct {
	ID        int64     `json:"id"`
	Category  *Category `json:"category" desc:"分类"`
	Name      string    `json:"name" required:"" example:"张三" desc:"名称"`
	PhotoUrls []string  `json:"photoUrls"`
	Tags      []string  `json:"tags" desc:"标签"`
}

func handle(w http.ResponseWriter, r *http.Request) {
	_, _ = io.WriteString(w, fmt.Sprintf("[%s] Hello World!", r.Method))
}

func main() {
	api := swag.New(
		option.Title("Example API Doc"),
		option.Security("petstore_auth", "read:pets"),
		option.SecurityScheme("petstore_auth",
			option.OAuth2Security("accessCode", "http://example.com/oauth/authorize", "http://example.com/oauth/token"),
			option.OAuth2Scope("write:pets", "modify pets in your account"),
			option.OAuth2Scope("read:pets", "read your pets"),
		),
	)
	api.AddEndpoint(
		endpoint.New(
			http.MethodPost, "/pet",
			endpoint.Handler(handle),
			endpoint.Summary("Add a new pet to the store"),
			endpoint.Description("Additional information on adding a pet to the store"),
			endpoint.Body(Pet{}, "Pet object that needs to be added to the store", true),
			endpoint.Response(http.StatusOK, "Successfully added pet", endpoint.Schema(Pet{})),
			endpoint.Security("petstore_auth", "read:pets", "write:pets"),
		),
		endpoint.New(
			http.MethodGet, "/pet/{petId}",
			endpoint.Handler(handle),
			endpoint.Summary("Find pet by ID"),
			endpoint.Path("petId", "integer", "ID of pet to return", true),
			endpoint.Response(http.StatusOK, "successful operation", endpoint.Schema(Pet{})),
			endpoint.Security("petstore_auth", "read:pets"),
		),
		endpoint.New(
			http.MethodPut, "/pet/{petId}",
			endpoint.Handler(handle),
			endpoint.Path("petId", "integer", "ID of pet to return", true),
			endpoint.Security("petstore_auth", "read:pets"),
			endpoint.ResponseSuccess(endpoint.Schema(struct {
				ID   string `json:"id"`
				Name string `json:"name"`
			}{})),
		),
	)

	...
}

built-in
func main() {
    ...
    // Note: Built-in routes cannot automatically resolve path parameters.
    for p, endpoints := range api.Paths {
        http.DefaultServeMux.Handle(path.Join(api.BasePath, p), endpoints)
    }
    http.DefaultServeMux.Handle("/swagger/json", api.Handler())
    patterns := swag.UIPatterns("/swagger/ui")
    for _, pattern := range patterns {
        http.DefaultServeMux.Handle(pattern, swag.UIHandler("/swagger/ui", "/swagger/json", true))
    }
    
    log.Fatal(http.ListenAndServe(":8080", nil))
}

gin
func main() {
    ...
    
    router := gin.New()
    api.Walk(func (path string, e *swag.Endpoint) {
        h := e.Handler.(http.Handler)
        path = swag.ColonPath(path)
        
        router.Handle(e.Method, path, gin.WrapH(h))
    })
    
    // Register Swagger JSON route
    router.GET("/swagger/json", gin.WrapH(api.Handler()))
    
    // Register Swagger UI route
    // To take effect, the swagger json route must be registered
    router.GET("/swagger/ui/*any", gin.WrapH(swag.UIHandler("/swagger/ui", "/swagger/json", true)))
    
    log.Fatal(http.ListenAndServe(":8080", router))
}
chi
func main() {
    ...
    
    router := chi.NewRouter()
    api.Walk(func (path string, e *swag.Endpoint) {
        router.Method(e.Method, path, e.Handler.(http.Handler))
    })
    router.Handle("/swagger/json", api.Handler())
    router.Mount("/swagger/ui", swag.UIHandler("/swagger/ui", "/swagger/json", true))
    
    log.Fatal(http.ListenAndServe(":8080", router))
}
mux
func main() {
    ...
    
    router := mux.NewRouter()
    api.Walk(func (path string, e *swag.Endpoint) {
        h := e.Handler.(http.HandlerFunc)
        router.Path(path).Methods(e.Method).Handler(h)
    })
    
    router.Path("/swagger/json").Methods("GET").Handler(api.Handler())
    router.PathPrefix("/swagger/ui").Handler(swag.UIHandler("/swagger/ui", "/swagger/json", true))
    
    log.Fatal(http.ListenAndServe(":8080", router))
}
echo
func main() {
    ...
    
    router := echo.New()
    api.Walk(func (path string, e *swag.Endpoint) {
        h := echo.WrapHandler(e.Handler.(http.Handler))
        path = swag.ColonPath(path)
        
        switch strings.ToLower(e.Method) {
        case "get":
            router.GET(path, h)
        case "head":
            router.HEAD(path, h)
        case "options":
            router.OPTIONS(path, h)
        case "delete":
            router.DELETE(path, h)
        case "put":
            router.PUT(path, h)
        case "post":
            router.POST(path, h)
        case "trace":
            router.TRACE(path, h)
        case "patch":
            router.PATCH(path, h)
        case "connect":
            router.CONNECT(path, h)
        }
    })
    
    router.GET("/swagger/json", echo.WrapHandler(api.Handler()))
    router.GET("/swagger/ui/*", echo.WrapHandler(swag.UIHandler("/swagger/ui", "/swagger/json", true)))
    
    log.Fatal(http.ListenAndServe(":8080", router))
}
httprouter
func main() {
    ...
    
    router := httprouter.New()
    api.Walk(func (path string, e *swag.Endpoint) {
        h := e.Handler.(http.Handler)
        path = swag.ColonPath(path)
        router.Handler(e.Method, path, h)
    })
    
    router.Handler(http.MethodGet, "/swagger/json", api.Handler())
    router.Handler(http.MethodGet, "/swagger/ui/*any", swag.UIHandler("/swagger/ui", "/swagger/json", true))
    
    log.Fatal(http.ListenAndServe(":8080", router))
}
fasthttp
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"path"
	"path/filepath"
	"strings"

	"github.com/fasthttp/router"
	"github.com/valyala/fasthttp"

	"github.com/zc2638/swag"
	"github.com/zc2638/swag/asserts"
	"github.com/zc2638/swag/endpoint"
	"github.com/zc2638/swag/option"
)

// Category example from the swagger pet store
type Category struct {
	ID     int64  `json:"category"`
	Name   string `json:"name" enum:"dog,cat" required:""`
	Exists *bool  `json:"exists" required:""`
}

// Pet example from the swagger pet store
type Pet struct {
	ID        int64     `json:"id"`
	Category  *Category `json:"category" desc:"分类"`
	Name      string    `json:"name" required:"" example:"张三" desc:"名称"`
	PhotoUrls []string  `json:"photoUrls"`
	Tags      []string  `json:"tags" desc:"标签"`
}

func handle(ctx *fasthttp.RequestCtx) {
	str := fmt.Sprintf("[%s] Hello World!", string(ctx.Method()))
	_, _ = ctx.Write([]byte(str))
}

func main() {
	post := endpoint.New("post", "/pet", endpoint.Summary("Add a new pet to the store"),
		endpoint.Handler(handle),
		endpoint.Description("Additional information on adding a pet to the store"),
		endpoint.Body(Pet{}, "Pet object that needs to be added to the store", true),
		endpoint.Response(http.StatusOK, "Successfully added pet", endpoint.Schema(Pet{})),
		endpoint.Security("petstore_auth", "read:pets", "write:pets"),
	)
	get := endpoint.New("get", "/pet/{petId}", endpoint.Summary("Find pet by ID"),
		endpoint.Handler(handle),
		endpoint.Path("petId", "integer", "ID of pet to return", true),
		endpoint.Response(http.StatusOK, "successful operation", endpoint.Schema(Pet{})),
		endpoint.Security("petstore_auth", "read:pets"),
	)
	test := endpoint.New("put", "/pet/{petId}",
		endpoint.Handler(handle),
		endpoint.Path("petId", "integer", "ID of pet to return", true),
		endpoint.Response(http.StatusOK, "successful operation", endpoint.Schema(struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		}{})),
		endpoint.Security("petstore_auth", "read:pets"),
	)

	api := swag.New(
		option.Title("Example API Doc"),
		option.Security("petstore_auth", "read:pets"),
		option.SecurityScheme("petstore_auth",
			option.OAuth2Security("accessCode", "http://example.com/oauth/authorize", "http://example.com/oauth/token"),
			option.OAuth2Scope("write:pets", "modify pets in your account"),
			option.OAuth2Scope("read:pets", "read your pets"),
		),
		option.Endpoints(post, get),
	)
	api.AddEndpoint(test)

	r := router.New()
	api.Walk(func(path string, e *swag.Endpoint) {
		if v, ok := e.Handler.(func(ctx *fasthttp.RequestCtx)); ok {
			r.Handle(e.Method, path, fasthttp.RequestHandler(v))
		} else {
			r.Handle(e.Method, path, e.Handler.(fasthttp.RequestHandler))
		}
	})

	buildSchemeFn := func(ctx *fasthttp.RequestCtx) string {
		var scheme []byte

		if ctx.IsTLS() {
			scheme = []byte("https")
		}
		if v := ctx.Request.Header.Peek("X-Forwarded-Proto"); v != nil {
			scheme = v
		}
		if string(scheme) == "" {
			scheme = ctx.URI().Scheme()
		}
		if string(scheme) == "" {
			scheme = []byte("http")
		}
		return string(scheme)
	}

	doc := api.Clone()
	r.GET("/swagger/json", func(ctx *fasthttp.RequestCtx) {
		scheme := buildSchemeFn(ctx)
		doc.Host = string(ctx.Host())
		doc.Schemes = []string{scheme}

		b, err := json.Marshal(doc)
		if err != nil {
			ctx.Error("Parse API Doc exceptions", http.StatusInternalServerError)
			return
		}
		_, _ = ctx.Write(b)
	})

	r.ANY("/swagger/ui/{any:*}", func(ctx *fasthttp.RequestCtx) {
		currentPath := strings.TrimPrefix(string(ctx.Path()), "/swagger/ui")

		if currentPath == "/" || currentPath == "index.html" {
			fullName := filepath.Join(asserts.DistDir, "index.html")
			fileData, err := asserts.Dist.ReadFile(fullName)
			if err != nil {
				ctx.Error("index.html read exception", http.StatusInternalServerError)
				return
			}

			scheme := buildSchemeFn(ctx)
			currentURI := scheme + "://" + path.Join(string(ctx.Host()), "/swagger/json")

			fileData = bytes.ReplaceAll(fileData, []byte(asserts.URL), []byte(currentURI))
			ctx.SetContentType("text/html; charset=utf-8")
			ctx.Write(fileData)
			return
		}
		sfs := swag.DirFS(asserts.DistDir, asserts.Dist)
		file, err := sfs.Open(currentPath)
		if err != nil {
			ctx.Error(err.Error(), http.StatusInternalServerError)
			return
		}

		stat, err := file.Stat()
		if err != nil {
			ctx.Error(err.Error(), http.StatusInternalServerError)
			return
		}

		switch strings.TrimPrefix(filepath.Ext(stat.Name()), ".") {
		case "css":
			ctx.SetContentType("text/css; charset=utf-8")
		case "js":
			ctx.SetContentType("application/javascript")
		}
		io.Copy(ctx, file)
	})

	fasthttp.ListenAndServe(":8080", r.Handler)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ColonPath

func ColonPath(path string) string

ColonPath accepts a swagger path.

e.g. /api/orgs/{org} and returns a colon identified path

e.g. /api/org/:org

func DirFS added in v1.2.0

func DirFS(dir string, fsys fs.FS) http.FileSystem

DirFS returns a http.FileSystem by the specified directory path and FS

func UIHandler added in v1.2.0

func UIHandler(prefix, uri string, autoDomain bool) http.Handler

UIHandler returns a http.Handler by the specify path prefix and the full path

func UIPatterns added in v1.2.0

func UIPatterns(prefix string) []string

UIPatterns returns a list of all the paths needed based on the path prefix

Types

type API added in v1.2.0

type API struct {
	Swagger             string                    `json:"swagger,omitempty"`
	Info                Info                      `json:"info"`
	BasePath            string                    `json:"basePath,omitempty"`
	Schemes             []string                  `json:"schemes,omitempty"`
	Paths               map[string]*Endpoints     `json:"paths,omitempty"`
	Definitions         map[string]Object         `json:"definitions,omitempty"`
	Tags                []Tag                     `json:"tags,omitempty"`
	Host                string                    `json:"host,omitempty"`
	SecurityDefinitions map[string]SecurityScheme `json:"securityDefinitions,omitempty"`
	Security            *SecurityRequirement      `json:"security,omitempty"`
	// contains filtered or unexported fields
}

API provides the top level encapsulation for the swagger definition

func New

func New(options ...Option) *API

New constructs a new api builder

func (*API) AddEndpoint added in v1.2.0

func (a *API) AddEndpoint(es ...*Endpoint)

AddEndpoint adds the specified endpoint to the API definition; to generate an endpoint use ```endpoint.New```

func (*API) AddEndpointFunc deprecated added in v1.2.0

func (a *API) AddEndpointFunc(fs ...func(*API))

AddEndpointFunc adds some options

Deprecated: please use the new AddOptions method

func (*API) AddOptions added in v1.4.0

func (a *API) AddOptions(options ...Option)

AddOptions adds some options

func (*API) AddTag added in v1.2.0

func (a *API) AddTag(name, description string)

func (*API) Clone added in v1.2.1

func (a *API) Clone() *API

func (*API) Handler added in v1.2.0

func (a *API) Handler() http.HandlerFunc

Handler is a factory method that generates an http.HandlerFunc; if enableCors is true, then the handler will generate cors headers

func (*API) Walk added in v1.2.0

func (a *API) Walk(callback func(path string, endpoint *Endpoint))

Walk invoke the callback for each endpoint defined in the swagger doc

func (*API) WithGroup added in v1.4.0

func (a *API) WithGroup(prefixPath string) *API

func (*API) WithTag added in v1.4.0

func (a *API) WithTag(name, description string) *API

func (*API) WithTags added in v1.2.0

func (a *API) WithTags(tags ...Tag) *API

type Contact added in v1.2.0

type Contact struct {
	Email string `json:"email,omitempty"`
}

Contact represents the contact entity from the swagger definition; used by Info

type Endpoint added in v1.2.0

type Endpoint struct {
	Tags        []string            `json:"tags,omitempty"`
	Path        string              `json:"-"`
	Method      string              `json:"-"`
	Summary     string              `json:"summary,omitempty"`
	Description string              `json:"description,omitempty"`
	OperationID string              `json:"operationId,omitempty"`
	Produces    []string            `json:"produces,omitempty"`
	Consumes    []string            `json:"consumes,omitempty"`
	Handler     interface{}         `json:"-"`
	Parameters  []Parameter         `json:"parameters,omitempty"`
	Responses   map[string]Response `json:"responses,omitempty"`

	// swagger spec requires security to be an array of objects
	Security   *SecurityRequirement `json:"security,omitempty"`
	Deprecated bool                 `json:"deprecated,omitempty"`
}

Endpoint represents an endpoint from the swagger doc

func (*Endpoint) BuildOperationID added in v1.4.0

func (e *Endpoint) BuildOperationID()

type Endpoints

type Endpoints struct {
	Delete  *Endpoint `json:"delete,omitempty"`
	Head    *Endpoint `json:"head,omitempty"`
	Get     *Endpoint `json:"get,omitempty"`
	Options *Endpoint `json:"options,omitempty"`
	Post    *Endpoint `json:"post,omitempty"`
	Put     *Endpoint `json:"put,omitempty"`
	Patch   *Endpoint `json:"patch,omitempty"`
	Trace   *Endpoint `json:"trace,omitempty"`
	Connect *Endpoint `json:"connect,omitempty"`
}

Endpoints represents all the swagger endpoints associated with a particular path

func (*Endpoints) ServeHTTP added in v1.2.0

func (e *Endpoints) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP allows endpoints to serve itself using the builtin http mux

func (*Endpoints) Walk added in v1.2.0

func (e *Endpoints) Walk(fn func(endpoint *Endpoint))

Walk calls the specified function for each method defined within the Endpoints

type Header struct {
	Type        types.ParameterType `json:"type"`
	Format      string              `json:"format"`
	Description string              `json:"description"`
}

Header represents a response header

type Info added in v1.2.0

type Info struct {
	Description    string   `json:"description,omitempty"`
	Version        string   `json:"version,omitempty"`
	TermsOfService string   `json:"termsOfService,omitempty"`
	Title          string   `json:"title,omitempty"`
	Contact        *Contact `json:"contact,omitempty"`
	License        License  `json:"license"`
}

Info represents the info entity from the swagger definition

type Items added in v1.2.0

type Items struct {
	Type   string `json:"type,omitempty"`
	Format string `json:"format,omitempty"`
	Ref    string `json:"$ref,omitempty"`
}

Items represents items from the swagger doc

type License

type License struct {
	Name string `json:"name,omitempty"`
	URL  string `json:"url,omitempty"`
}

License represents the license entity from the swagger definition; used by Info

type Object added in v1.2.0

type Object struct {
	IsArray     bool                `json:"-"`
	GoType      reflect.Type        `json:"-"`
	Name        string              `json:"-"`
	Type        string              `json:"type"`
	Description string              `json:"description,omitempty"`
	Format      string              `json:"format,omitempty"`
	Required    []string            `json:"required,omitempty"`
	Properties  map[string]Property `json:"properties,omitempty"`
}

Object represents the object entity from the swagger definition

type Option

type Option func(api *API)

Option provides configuration options to the swagger api

type Parameter added in v1.2.0

type Parameter struct {
	In          string              `json:"in,omitempty"`
	Name        string              `json:"name,omitempty"`
	Description string              `json:"description,omitempty"`
	Required    bool                `json:"required"`
	Schema      *Schema             `json:"schema,omitempty"`
	Type        types.ParameterType `json:"type,omitempty"`
	Format      string              `json:"format,omitempty"`
	Default     string              `json:"default,omitempty"`
}

Parameter represents a parameter from the swagger doc

type Property added in v1.2.0

type Property struct {
	GoType      reflect.Type `json:"-"`
	Type        string       `json:"type,omitempty"`
	Description string       `json:"description,omitempty"`
	Enum        []string     `json:"enum,omitempty"`
	Format      string       `json:"format,omitempty"`
	Ref         string       `json:"$ref,omitempty"`
	Example     string       `json:"example,omitempty"`
	Items       *Items       `json:"items,omitempty"`
}

Property represents the property entity from the swagger definition

type Response added in v1.2.0

type Response struct {
	Description string            `json:"description"`
	Schema      *Schema           `json:"schema,omitempty"`
	Headers     map[string]Header `json:"headers,omitempty"`
}

Response represents a response from the swagger doc

type Schema added in v1.2.0

type Schema struct {
	Type      string      `json:"type,omitempty"`
	Items     *Items      `json:"items,omitempty"`
	Ref       string      `json:"$ref,omitempty"`
	Prototype interface{} `json:"-"`
}

Schema represents a schema from the swagger doc

func MakeSchema added in v1.2.0

func MakeSchema(prototype interface{}) *Schema

MakeSchema takes struct or pointer to a struct and returns a Schema instance suitable for use by the swagger doc

type SecurityRequirement added in v1.2.0

type SecurityRequirement struct {
	Requirements    []map[string][]string
	DisableSecurity bool
}

func (*SecurityRequirement) MarshalJSON added in v1.2.0

func (s *SecurityRequirement) MarshalJSON() ([]byte, error)

type SecurityScheme

type SecurityScheme struct {
	Type             string            `json:"type"`
	Description      string            `json:"description,omitempty"`
	Name             string            `json:"name,omitempty"`
	In               string            `json:"in,omitempty"`
	Flow             string            `json:"flow,omitempty"`
	AuthorizationURL string            `json:"authorizationUrl,omitempty"`
	TokenURL         string            `json:"tokenUrl,omitempty"`
	Scopes           map[string]string `json:"scopes,omitempty"`
}

SecurityScheme represents a security scheme from the swagger definition.

type Tag

type Tag struct {
	Name        string   `json:"name"`
	Description string   `json:"description"`
	Docs        *TagDocs `json:"externalDocs,omitempty"`
}

Tag represents a swagger tag

type TagDocs added in v1.2.0

type TagDocs struct {
	Description string `json:"description"`
	URL         string `json:"url"`
}

TagDocs represents tag docs from the swagger definition

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL