这是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
32 changes: 30 additions & 2 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ type ResolverWithPath interface {
Resolve(ctx Context, prefix *PathBuffer) []error
}

var resolverType = reflect.TypeOf((*Resolver)(nil)).Elem()
var resolverWithPathType = reflect.TypeOf((*ResolverWithPath)(nil)).Elem()
var (
resolverType = reflect.TypeOf((*Resolver)(nil)).Elem()
resolverWithPathType = reflect.TypeOf((*ResolverWithPath)(nil)).Elem()
)

// Adapter is an interface that allows the API to be used with different HTTP
// routers and frameworks. It is designed to work with the standard library
Expand Down Expand Up @@ -108,6 +110,32 @@ type Context interface {
BodyWriter() io.Writer
}

type (
humaContext Context
subContext struct {
humaContext
override context.Context
}
)

func (c subContext) Context() context.Context {
return c.override
}

// WithContext returns a new `huma.Context` with the underlying `context.Context`
// replaced with the given one. This is useful for middleware that needs to
// modify the request context.
func WithContext(ctx Context, override context.Context) Context {
return subContext{humaContext: ctx, override: override}
}

// WithValue returns a new `huma.Context` with the given key and value set in
// the underlying `context.Context`. This is useful for middleware that needs to
// set request-scoped values.
func WithValue(ctx Context, key, value any) Context {
return WithContext(ctx, context.WithValue(ctx.Context(), key, value))
}

// Transformer is a function that can modify a response body before it is
// serialized. The `status` is the HTTP status code for the response and `v` is
// the value to be serialized. The return value is the new value to be
Expand Down
20 changes: 20 additions & 0 deletions api_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package huma_test

import (
"context"
"net/http"
"testing"

Expand Down Expand Up @@ -67,3 +68,22 @@ func ExampleAdapter_handle() {
ctx.BodyWriter().Write([]byte("Hello, " + name))
})
}

func TestContextValue(t *testing.T) {
_, api := humatest.New(t)

api.UseMiddleware(func(ctx huma.Context, next func(huma.Context)) {
// Make an updated context available to the handler.
ctx = huma.WithValue(ctx, "foo", "bar")
next(ctx)
})

// Register a simple hello world operation in the API.
huma.Get(api, "/test", func(ctx context.Context, input *struct{}) (*struct{}, error) {
assert.Equal(t, "bar", ctx.Value("foo"))
return nil, nil
})

resp := api.Get("/test")
assert.Equal(t, http.StatusNoContent, resp.Code)
}
23 changes: 23 additions & 0 deletions docs/docs/features/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@ func NewHumaAPI() huma.API {
}
```

### Context Values

The `huma.Context` interface provides a `Context()` method to retrieve the underlying request `context.Context` value. This can be used to retrieve context values in middleware and operation handlers, such as request-scoped loggers, metrics, or user information.

```go title="code.go"
if v, ok := ctx.Context().Value("some-key").(string); ok {
// Do something with `v`!
}
```

You can also wrap the `huma.Context` to provide additional or override functionality. Some utilities are provided for this, including [`huma.WithValue`](https://pkg.go.dev/github.com/danielgtaylor/huma/v2#WithValue):

```go title="code.go"
func MyMiddleware(ctx huma.Context, next func(huma.Context)) {
// Wrap the context to add a value.
ctx = huma.WithValue(ctx, "some-key", "some-value")

// Call the next middleware in the chain. This eventually calls the
// operation handler as well.
next(ctx)
}
```

### Cookies

You can use the `huma.Context` interface along with [`huma.ReadCookie`](https://pkg.go.dev/github.com/danielgtaylor/huma/v2#ReadCookie) or [`huma.ReadCookies`](https://pkg.go.dev/github.com/danielgtaylor/huma/v2#ReadCookies) to access cookies from middleware, and can also write cookies by adding `Set-Cookie` headers in the response:
Expand Down