这是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
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,40 @@ http.ListenAndServe(":8888", r)

### Middleware

Huma v1 came with its own middleware, but v2 does not. You can use any middleware you want, or even write your own. This is for two reasons:
Huma v2 has support two variants of middlewares:

1. Middleware is often router-specific and Huma is designed to be router-agnostic.
2. Many organizations already have a set of middleware for logging, metrics, distributed tracing, panic recovery, etc.
1. Router-specific - works at the router level, i.e. before router-specific middleware, you can use any middleware that is implemented for your router.
2. Router-agnostic - runs in the Huma processing chain, i.e. after calls to router-specific middleware.

#### Router-specific
Each router implementation has its own middlewares, you can use this middlewares with huma v2 framework.

Chi router example:
```go
router := chi.NewMux()
router.Use(jwtauth.Verifier(tokenAuth))
api := humachi.New(router, defconfig)
```
> :whale: Huma v1 middleware is compatible with Chi, so if you use that router with v2 you can continue to use the v1 middleware in a v2 application.


#### Router-agnostic
You can write you own huma v2 middleware without dependency to router implementation.

Example:
```go
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
// I don't do anything
next(ctx)
}
func NewHumaAPI() huma.API {
// ...
api := humachi.New(router, config)
// OR api := humagin.New(router, config)
api.UseMiddleware(MyMiddleware)
}
```

## Open API Generation & Extensibility

Huma generates Open API 3.1.0 compatible JSON/YAML specs and provides rendered documentation automatically. Every operation that is registered with the API is included in the spec by default. The operation's inputs and outputs are used to generate the request and response parameters / schemas.
Expand Down
20 changes: 20 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ type API interface {

// Unmarshal unmarshals the given data into the given value. The content type
Unmarshal(contentType string, data []byte, v any) error

// UseMiddleware appends a middleware handler to the API middleware stack.
//
// The middleware stack for any API will execute before searching for a matching
// route to a specific handler, which provides opportunity to respond early,
// change the course of the request execution, or set request-scoped values for
// the next Middleware.
UseMiddleware(middlewares ...func(ctx Context, next func(Context)))

// Middlewares returns a slice of middleware handler functions.
Middlewares() Middlewares
}

// Format represents a request / response format. It is used to marshal and
Expand All @@ -141,6 +152,7 @@ type api struct {
formats map[string]Format
formatKeys []string
transformers []Transformer
middlewares Middlewares
}

func (a *api) Adapter() Adapter {
Expand Down Expand Up @@ -202,6 +214,14 @@ func (a *api) Marshal(ctx Context, respKey string, ct string, v any) error {
return f.Marshal(ctx.BodyWriter(), v)
}

func (a *api) UseMiddleware(middlewares ...func(ctx Context, next func(Context))) {
a.middlewares = append(a.middlewares, middlewares...)
}

func (a *api) Middlewares() Middlewares {
return a.middlewares
}

func NewAPI(config Config, a Adapter) API {
newAPI := &api{
config: config,
Expand Down
32 changes: 32 additions & 0 deletions chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package huma

type Middlewares []func(ctx Context, next func(Context))

// Handler builds and returns a handler func from the chain of middlewares,
// with `endpoint func` as the final handler.
func (m Middlewares) Handler(endpoint func(Context)) func(Context) {
return m.chain(endpoint)
}

// wrap user middleware func with the next func to one func
func wrap(fn func(Context, func(Context)), next func(Context)) func(Context) {
return func(ctx Context) {
fn(ctx, next)
}
}

// chain builds a Middleware composed of an inline middleware stack and endpoint
// handler in the order they are passed.
func (m Middlewares) chain(endpoint func(Context)) func(Context) {
// Return ahead of time if there aren't any middlewares for the chain
if len(m) == 0 {
return endpoint
}

// Wrap the end handler with the middleware chain
w := wrap(m[len(m)-1], endpoint)
for i := len(m) - 2; i >= 0; i-- {
w = wrap(m[i], w)
}
return w
}
4 changes: 2 additions & 2 deletions huma.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I)

a := api.Adapter()

a.Handle(&op, func(ctx Context) {
a.Handle(&op, api.Middlewares().Handler(func(ctx Context) {
var input I

// Get the validation dependencies from the shared pool.
Expand Down Expand Up @@ -856,7 +856,7 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I)
} else {
ctx.SetStatus(status)
}
})
}))
}

// AutoRegister auto-detects operation registration methods and registers them
Expand Down