这是indexloc提供的服务,不要输入任何密码
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 34 additions & 22 deletions docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ func splitDocs(docs string) (title, desc string) {
return
}

// RapiDocHandler renders documentation using RapiDoc.
func RapiDocHandler(pageTitle string) Handler {
return func(c *gin.Context) {
c.Data(200, "text/html", []byte(fmt.Sprintf(`<!doctype html>
// RapiDocTemplate is the template used to generate the RapiDoc. It needs two args to render:
// 1. the title
// 2. the path to the openapi.yaml file
var RapiDocTemplate = `<!doctype html>
<html>
<head>
<title>%s</title>
Expand All @@ -31,21 +31,19 @@ func RapiDocHandler(pageTitle string) Handler {
</head>
<body>
<rapi-doc
spec-url="/openapi.json"
spec-url="%s"
render-style="read"
show-header="false"
primary-color="#f74799"
nav-accent-color="#47afe8"
> </rapi-doc>
</body>
</html>`, pageTitle)))
}
}
</html>`

// ReDocHandler renders documentation using ReDoc.
func ReDocHandler(pageTitle string) Handler {
return func(c *gin.Context) {
c.Data(200, "text/html", []byte(fmt.Sprintf(`<!DOCTYPE html>
// ReDocTemplate is the template used to generate the RapiDoc. It needs two args to render:
// 1. the title
// 2. the path to the openapi.yaml file
var ReDocTemplate = `<!DOCTYPE html>
<html>
<head>
<title>%s</title>
Expand All @@ -55,17 +53,12 @@ func ReDocHandler(pageTitle string) Handler {
<style>body { margin: 0; padding: 0; }</style>
</head>
<body>
<redoc spec-url='/openapi.json'></redoc>
<redoc spec-url='%s'></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
</body>
</html>`, pageTitle)))
}
}
</html>`

// SwaggerUIHandler renders documentation using Swagger UI.
func SwaggerUIHandler(pageTitle string) Handler {
return func(c *gin.Context) {
c.Data(200, "text/html", []byte(fmt.Sprintf(`<!-- HTML for static distribution bundle build -->
var SwaggerUITemplate = `<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
Expand Down Expand Up @@ -104,7 +97,7 @@ func SwaggerUIHandler(pageTitle string) Handler {
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "/openapi.json",
url: "%s",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
Expand All @@ -122,6 +115,25 @@ func SwaggerUIHandler(pageTitle string) Handler {
}
</script>
</body>
</html>`, pageTitle)))
</html>`

// RapiDocHandler renders documentation using RapiDoc.
func RapiDocHandler(pageTitle string) Handler {
return func(c *gin.Context) {
c.Data(200, "text/html", []byte(fmt.Sprintf(RapiDocTemplate, pageTitle, "/openapi.json")))
}
}

// ReDocHandler renders documentation using ReDoc.
func ReDocHandler(pageTitle string) Handler {
return func(c *gin.Context) {
c.Data(200, "text/html", []byte(fmt.Sprintf(ReDocTemplate, pageTitle, "/openapi.json")))
}
}

// SwaggerUIHandler renders documentation using Swagger UI.
func SwaggerUIHandler(pageTitle string) Handler {
return func(c *gin.Context) {
c.Data(200, "text/html", []byte(fmt.Sprintf(SwaggerUITemplate, pageTitle, "/openapi.json")))
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0
go.uber.org/zap v1.10.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.8
)
18 changes: 18 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ func ContactEmail(name, email string) RouterOption {
}}
}

// DocsRoutePrefix enables the API documentation to be available from `prefix/{docs, openapi.yaml}`
func DocsRoutePrefix(prefix string) RouterOption {
return &routerOption{func(r *Router) {
r.docsPrefix = prefix
}}
}

// BasicAuth adds a named HTTP Basic Auth security scheme.
func BasicAuth(name string) RouterOption {
return &routerOption{func(r *Router) {
Expand Down Expand Up @@ -369,12 +376,23 @@ func HTTPServer(server *http.Server) RouterOption {
// DocsHandler sets the documentation rendering handler function. You can
// use `huma.RapiDocHandler`, `huma.ReDocHandler`, `huma.SwaggerUIHandler`, or
// provide your own (e.g. with custom auth or branding).
//
// DEPRECATED! Use `DocsDomType` instead!
func DocsHandler(f Handler) RouterOption {
fmt.Println("This option is deprecated, use `DocsDomType` instead")
return &routerOption{func(r *Router) {
r.docsHandler = f
}}
}

// DocsDomType sets the presentation for the docs UI. Valid values are:
// "rapi" (default), "redoc", or "swagger"
func DocsDomType(t string) RouterOption {
return &routerOption{func(r *Router) {
r.docsDomType = t
}}
}

// CORSHandler sets the CORS handler function. This can be used to set custom
// domains, headers, auth, etc. If not given, then a default CORS handler is
// used instead.
Expand Down
24 changes: 19 additions & 5 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ type Router struct {
root *cobra.Command
prestart []func()
docsHandler Handler
docsDomType string
docsPrefix string
corsHandler Handler

// Tracks the currently running server for graceful shutdown.
Expand Down Expand Up @@ -357,6 +359,8 @@ func NewRouter(docs, version string, options ...RouterOption) *Router {
engine: g,
prestart: []func(){},
docsHandler: RapiDocHandler(title),
docsDomType: "rapi",
docsPrefix: "",
corsHandler: cors.Default(),
}

Expand All @@ -378,11 +382,21 @@ func NewRouter(docs, version string, options ...RouterOption) *Router {
}

// Set up handlers for the auto-generated spec and docs.
r.engine.GET("/openapi.json", openAPIHandlerJSON(r))
r.engine.GET("/openapi.yaml", openAPIHandlerYAML(r))

r.engine.GET("/docs", func(c *gin.Context) {
r.docsHandler(c)
openapiJsonPath := fmt.Sprintf("%s/openapi.json", r.docsPrefix)
r.engine.GET(openapiJsonPath, openAPIHandlerJSON(r))
r.engine.GET(fmt.Sprintf("%s/openapi.yaml", r.docsPrefix), openAPIHandlerYAML(r))

r.engine.GET(fmt.Sprintf("%s/docs", r.docsPrefix), func(c *gin.Context) {
docsPayload := ""
switch r.docsDomType {
case "rapi":
docsPayload = fmt.Sprintf(RapiDocTemplate, title, openapiJsonPath)
case "swagger":
docsPayload = fmt.Sprintf(SwaggerUITemplate, title, openapiJsonPath)
case "redoc":
docsPayload = fmt.Sprintf(ReDocTemplate, title, openapiJsonPath)
}
c.Data(200, "text/html", []byte(docsPayload))
})

// If downloads like a CLI or SDKs are available, serve them automatically
Expand Down
18 changes: 18 additions & 0 deletions router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,24 @@ func TestRouter(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code)
}

func TestRouterDocsPrefix(t *testing.T) {

r := NewRouter("api", "v", DocsRoutePrefix("/prefix"))
r.Resource("/hello").Get("doc", func() string { return "Hello" })

// Check spec & docs routes
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/prefix/openapi.json", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)

w = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodGet, "/prefix/docs", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "prefix/openapi")
}

func TestRouterRequestBody(t *testing.T) {
type EchoRequest struct {
Value string `json:"value"`
Expand Down