Skip to content

Commit 4772177

Browse files
committed
new context wrapper
1 parent 3bd3567 commit 4772177

File tree

4 files changed

+168
-0
lines changed

4 files changed

+168
-0
lines changed

HISTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
2323

2424
Changes apply to `main` branch.
2525

26+
- A new way to customize the handler's parameter among with the `hero` and `mvc` packages. New `iris.NewContextWrapper` method and `iris.DefaultContextPool` struct were added to wrap a handler and use a custom context instead of the iris.Context directly.
27+
2628
- The `cache` sub-package has an update, after 4 years:
2729

2830
- Add support for custom storage on `cache` package, through the `Handler#Store` method.

_examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* [Import from TOML](configuration/from-toml-file/main.go)
4949
* [Multi Environment Configuration](configuration/multi-environments) **NEW**
5050
* Routing
51+
* [Custom Context](routing/custom-context/main.go) **HOT/NEW**
5152
* [Party Controller](routing/party-controller) **NEW**
5253
* [Overview](routing/overview/main.go)
5354
* [Basic](routing/basic/main.go)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package main
2+
3+
import (
4+
"github.com/kataras/iris/v12"
5+
)
6+
7+
func main() {
8+
// 1. Create the iris app instance.
9+
app := iris.New()
10+
11+
/*
12+
w := iris.NewContextWrapper(&iris.DefaultContextPool[*myCustomContext]{
13+
AcquireFunc: func(ctx iris.Context) *myCustomContext {
14+
return &myCustomContext{
15+
Context: ctx,
16+
// custom fields here...
17+
}
18+
},
19+
ReleaseFunc: func(t *myCustomContext) {
20+
// do nothing
21+
},
22+
})
23+
OR: */
24+
// 2. Create the Context Wrapper which will be used to wrap the handlers
25+
// that expect a *myCustomContext instead of iris.Context.
26+
w := iris.NewContextWrapper(&myCustomContextPool{})
27+
28+
// 3. Register the handler(s) which expects a *myCustomContext instead of iris.Context.
29+
// The `w.Handler` will wrap the handler and will call the `Acquire` and `Release`
30+
// methods of the `myCustomContextPool` to get and release the *myCustomContext.
31+
app.Get("/", w.Handler(index))
32+
33+
// 4. Start the server.
34+
app.Listen(":8080")
35+
}
36+
37+
func index(ctx *myCustomContext) {
38+
ctx.HTML("<h1>Hello, World!</h1>")
39+
}
40+
41+
// Create a custom context.
42+
type myCustomContext struct {
43+
// It's just an embedded field which is set on AcquireFunc,
44+
// so you can use myCustomContext with the same methods as iris.Context,
45+
// override existing iris.Context's methods or add custom methods.
46+
// You can use the `Context` field to access the original context.
47+
iris.Context
48+
}
49+
50+
func (c *myCustomContext) HTML(format string, args ...interface{}) (int, error) {
51+
c.Application().Logger().Info("HTML was called from custom Context")
52+
53+
return c.Context.HTML(format, args...)
54+
}
55+
56+
// Create the context memory pool for your custom context,
57+
// the pool must contain Acquire() T and Release(T) methods.
58+
type myCustomContextPool struct{}
59+
60+
// Acquire returns a new custom context from the pool.
61+
func (p *myCustomContextPool) Acquire(ctx iris.Context) *myCustomContext {
62+
return &myCustomContext{
63+
Context: ctx,
64+
// custom fields here...
65+
}
66+
}
67+
68+
// Release puts a custom context back to the pool.
69+
func (p *myCustomContextPool) Release(t *myCustomContext) {
70+
// You can take advantage of this method to clear the context
71+
// and re-use it on the Acquire method, use the sync.Pool.
72+
//
73+
// We do nothing for the shake of the exampel.
74+
}

context_wrapper.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package iris
2+
3+
// ContextPool is a pool of T.
4+
//
5+
// See `NewContextWrapper` and `ContextPool` for more.
6+
type ContextPool[T any] interface {
7+
Acquire(ctx Context) T
8+
Release(T)
9+
}
10+
11+
// DefaultContextPool is a pool of T.
12+
// It's used to acquire and release T.
13+
// The T is acquired from the pool and released back to the pool after the handler's execution.
14+
// The T is passed to the handler as an argument.
15+
// The T is not shared between requests.
16+
type DefaultContextPool[T any] struct {
17+
AcquireFunc func(Context) T
18+
ReleaseFunc func(T)
19+
}
20+
21+
// Ensure that DefaultContextPool[T] implements ContextPool[T].
22+
var _ ContextPool[any] = (*DefaultContextPool[any])(nil)
23+
24+
// Acquire returns a new T from the pool's AcquireFunc.
25+
func (p *DefaultContextPool[T]) Acquire(ctx Context) T {
26+
acquire := p.AcquireFunc
27+
if p.AcquireFunc == nil {
28+
acquire = func(ctx Context) T {
29+
var t T
30+
return t
31+
}
32+
}
33+
34+
return acquire(ctx)
35+
}
36+
37+
// Release does nothing if the pool's ReleaseFunc is nil.
38+
func (p *DefaultContextPool[T]) Release(t T) {
39+
release := p.ReleaseFunc
40+
if p.ReleaseFunc == nil {
41+
release = func(t T) {}
42+
}
43+
44+
release(t)
45+
}
46+
47+
// ContextWrapper is a wrapper for handlers which expect a T instead of iris.Context.
48+
//
49+
// See the `NewContextWrapper` function for more.
50+
type ContextWrapper[T any] struct {
51+
pool ContextPool[T]
52+
}
53+
54+
// NewContextWrapper returns a new ContextWrapper.
55+
// If pool is nil, a default pool is used.
56+
// The default pool's AcquireFunc returns a zero value of T.
57+
// The default pool's ReleaseFunc does nothing.
58+
// The default pool is used when the pool is nil.
59+
// Use the `&iris.DefaultContextPool{...}` to pass a simple context pool.
60+
//
61+
// See the `Handler` method for more.
62+
// Example: https://github.com/kataras/iris/tree/main/_examples/routing/custom-context
63+
func NewContextWrapper[T any](pool ContextPool[T]) *ContextWrapper[T] {
64+
if pool == nil {
65+
pool = &DefaultContextPool[T]{
66+
AcquireFunc: func(ctx Context) T {
67+
var t T
68+
return t
69+
},
70+
ReleaseFunc: func(t T) {},
71+
}
72+
}
73+
74+
return &ContextWrapper[T]{
75+
pool: pool,
76+
}
77+
}
78+
79+
// Handler wraps the handler with the pool's Acquire and Release methods.
80+
// It returns a new handler which expects a T instead of iris.Context.
81+
// The T is the type of the pool.
82+
// The T is acquired from the pool and released back to the pool after the handler's execution.
83+
// The T is passed to the handler as an argument.
84+
// The T is not shared between requests.
85+
func (w *ContextWrapper[T]) Handler(handler func(T)) Handler {
86+
return func(ctx Context) {
87+
newT := w.pool.Acquire(ctx)
88+
handler(newT)
89+
w.pool.Release(newT)
90+
}
91+
}

0 commit comments

Comments
 (0)