这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
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
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -878,13 +878,21 @@ app.DocsHandler(huma.ReDocHandler(app.Router))

## OpenAPI

By default, the generated OpenAPI and autogenerated documentation are served in the root at `/openapi.json` and `/docs` respectively. This default path can be modified:
By default, the generated OpenAPI spec, schemas, and autogenerated documentation are served in the root at `/openapi.json`, `/schemas`, and `/docs` respectively. The default prefix for all, and the suffix for each individual route can be modified:

```go
// Serve `/public/openapi.json and `/public/docs`:
// Serve `/public/openapi.json`, `/public/schemas`, and `/public/docs`:
app.DocsPrefix("/public")
```

```go
// Serve `/internal/app/myService/model/openapi.json`, `/internal/app/myService/model/schemas`, and `/internal/app/myService/documentation`:
app.DocsPrefix("/internal/app/myService")
app.DocsSuffix("documentation")
app.SchemasSuffix("model/schemas")
app.SpecSuffix("model/openapi")
```

### Custom OpenAPI Fields

Use the OpenAPI hook for OpenAPI customization. It gives you a `*gabs.Container` instance that represents the root of the OpenAPI document.
Expand All @@ -902,7 +910,7 @@ app.OpenAPIHook(modify)

### JSON Schema

Each resource operation also returns a `describedby` HTTP link relation which references a JSON-Schema file. These schemas re-use the `DocsPrefix` described above and default to the server root. For example:
Each resource operation also returns a `describedby` HTTP link relation which references a JSON-Schema file. These schemas re-use the `DocsPrefix` and `SchemasSuffix` described above and default to the server root. For example:

```http
Link: </schemas/Note.json>; rel="describedby"
Expand Down
8 changes: 5 additions & 3 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ type hcontext struct {
errorCode int
op *Operation
closed bool
docsPrefix string
docsPath string
schemasPath string
specPath string
urlPrefix string
disableSchemaProperty bool
}
Expand Down Expand Up @@ -310,7 +312,7 @@ func (c *hcontext) writeModel(ct string, status int, model interface{}) {
if link != "" {
link += ", "
}
link += "<" + c.docsPrefix + "/schemas/" + id + ".json>; rel=\"describedby\""
link += "<" + c.schemasPath + "/" + id + ".json>; rel=\"describedby\""
c.Header().Set("Link", link)

if modelType.Kind() == reflect.Ptr {
Expand All @@ -320,7 +322,7 @@ func (c *hcontext) writeModel(ct string, status int, model interface{}) {
tmp := map[string]interface{}{}
shallowStructToMap(reflect.ValueOf(model), tmp)
if tmp["$schema"] == nil {
tmp["$schema"] = c.URLPrefix() + c.docsPrefix + "/schemas/" + id + ".json"
tmp["$schema"] = c.URLPrefix() + c.schemasPath + "/" + id + ".json"
}
model = tmp
}
Expand Down
4 changes: 3 additions & 1 deletion operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@ func (o *Operation) Run(handler interface{}) {
ResponseWriter: w,
r: r,
op: o,
docsPrefix: o.resource.router.docsPrefix,
docsPath: o.resource.router.DocsPath(),
schemasPath: o.resource.router.SchemasPath(),
specPath: o.resource.router.OpenAPIPath(),
urlPrefix: o.resource.router.urlPrefix,
disableSchemaProperty: o.resource.router.disableSchemaProperty,
errorCode: http.StatusBadRequest,
Expand Down
67 changes: 55 additions & 12 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ type Router struct {

autoConfig *AutoConfig

// Documentation handler function
docsPrefix string
docsHandler http.Handler
docsAreSetup bool
// Documentation, OpenAPI spec, and schemas routing handlers
docsPrefix string
docsSuffix string
schemasSuffix string
specSuffix string
docsHandler http.Handler
docsAreSetup bool

// Tracks the currently running server for graceful shutdown.
server *http.Server
Expand Down Expand Up @@ -215,17 +218,48 @@ func (r *Router) Middleware(middlewares ...func(next http.Handler) http.Handler)
r.mux.Use(middlewares...)
}

// DocsPath returns the server path to the OpenAPI docs.
func (r *Router) DocsPath() string {
return fmt.Sprintf("%s/%s", r.docsPrefix, r.docsSuffix)
}

// SchemasPath returns the server path to the OpenAPI Schemas.
func (r *Router) SchemasPath() string {
return fmt.Sprintf("%s/%s", r.docsPrefix, r.schemasSuffix)
}

// OpenAPIPath returns the server path to the OpenAPI JSON.
func (r *Router) OpenAPIPath() string {
return r.docsPrefix + "/openapi.json"
return fmt.Sprintf("%s/%s.json", r.docsPrefix, r.specSuffix)
}

// DocsPrefix sets the path prefix for where the OpenAPI JSON and documentation
// are hosted.
// DocsPrefix sets the path prefix for where the OpenAPI JSON, schemas,
// and documentation are hosted.
func (r *Router) DocsPrefix(path string) {
r.docsPrefix = path
}

// DocsSuffix sets the final path suffix for where the OpenAPI documentation
// is hosted. When not specified, the default value of `docs` is appended to the
// DocsPrefix.
func (r *Router) DocsSuffix(suffix string) {
r.docsSuffix = suffix
}

// SchemasSuffix sets the final path suffix for where the OpenAPI schemas
// are hosted. When not specified, the default value of `schemas` is appended
// to the DocsPrefix.
func (r *Router) SchemasSuffix(suffix string) {
r.schemasSuffix = suffix
}

// SpecSuffix sets the final path suffix for where the OpenAPI spec is hosted.
// When not specified, the default value of `openapi` is appended to the
// DocsPrefix.
func (r *Router) SpecSuffix(suffix string) {
r.specSuffix = suffix
}

// DocsHandler sets the http.Handler to render documentation. It defaults to
// using RapiDoc.
func (r *Router) DocsHandler(handler http.Handler) {
Expand Down Expand Up @@ -281,8 +315,8 @@ func (r *Router) setupDocs() {
})
}

if !r.mux.Match(chi.NewRouteContext(), http.MethodGet, r.docsPrefix+"/schemas/{schema-id}.json") {
r.mux.Get(r.docsPrefix+"/schemas/{schema-id}.json", func(w http.ResponseWriter, req *http.Request) {
if !r.mux.Match(chi.NewRouteContext(), http.MethodGet, r.SchemasPath()+"/{schema-id}.json") {
r.mux.Get(r.SchemasPath()+"/{schema-id}.json", func(w http.ResponseWriter, req *http.Request) {
id := chi.URLParam(req, "schema-id")
schema := schemas[id]
if schema == nil {
Expand All @@ -295,8 +329,8 @@ func (r *Router) setupDocs() {
})
}

if !r.mux.Match(chi.NewRouteContext(), http.MethodGet, r.docsPrefix+"/docs") {
r.mux.Get(r.docsPrefix+"/docs", r.docsHandler.ServeHTTP)
if !r.mux.Match(chi.NewRouteContext(), http.MethodGet, r.DocsPath()) {
r.mux.Get(r.DocsPath(), r.docsHandler.ServeHTTP)
}

r.docsAreSetup = true
Expand Down Expand Up @@ -411,6 +445,12 @@ func (r *Router) DisableSchemaProperty() {
r.disableSchemaProperty = true
}

const (
DefaultDocsSuffix = "docs"
DefaultSchemasSuffix = "schemas"
DefaultSpecSuffix = "openapi"
)

// New creates a new Huma router to which you can attach resources,
// operations, middleware, etc.
func New(docs, version string) *Router {
Expand All @@ -427,6 +467,9 @@ func New(docs, version string) *Router {
security: []map[string][]string{},
defaultBodyReadTimeout: 15 * time.Second,
defaultServerIdleTimeout: 15 * time.Second,
docsSuffix: DefaultDocsSuffix,
schemasSuffix: DefaultSchemasSuffix,
specSuffix: DefaultSpecSuffix,
}

r.docsHandler = RapiDocHandler(r)
Expand Down Expand Up @@ -456,7 +499,7 @@ func New(docs, version string) *Router {
if link != "" {
link += ", "
}
link += `<` + r.OpenAPIPath() + `>; rel="service-desc", <` + r.docsPrefix + `/docs>; rel="service-doc"`
link += `<` + r.OpenAPIPath() + `>; rel="service-desc", <` + r.DocsPath() + `>; rel="service-doc"`
w.Header().Set("link", link)
}
})
Expand Down