Addition of Locals Function with Go Generics as an Alternative to c.Locals (#2813)

* Add type-specific local value handling with generics in Ctx

Introduced a new function, Locals, that utilizes Go's generics to handle and retrieve type-specific local values within a request context. This enhancement provides more accurate data type control within the context. Included are tests for generic and custom struct use-cases to ensure the function performs as expected.

* Update documentation for Go generics in Locals method

Added documentation to explain the new version of the Locals method that uses Go's generics feature. This version allows for better control of data types when manipulating and retrieving local values within a request's context. Examples are provided, along with a caution on using correct data types to prevent a runtime panic.

* update ctx.md

* Correct indentation in API documentation

* Refactor Locals function and add new test case

* Refactor Locals function and add new test case

---------

Co-authored-by: Deza Farras Tsany <deza.ftsany@gmail.com>
pull/2820/head
ACHMAD IRIANTO EKA PUTRA 2024-01-29 17:26:31 +07:00 committed by GitHub
parent 31246ffe24
commit 738e062d5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 86 additions and 0 deletions

16
ctx.go
View File

@ -817,6 +817,22 @@ func (c *DefaultCtx) Locals(key any, value ...any) any {
return value[0]
}
// Locals function utilizing Go's generics feature.
// This function allows for manipulating and retrieving local values within a request context with a more specific data type.
func Locals[V any](c Ctx, key any, value ...V) V {
var v V
var ok bool
if len(value) == 0 {
v, ok = c.Locals(key).(V)
} else {
v, ok = c.Locals(key, value[0]).(V)
}
if !ok {
return v // return zero of type V
}
return v
}
// Location sets the response Location HTTP header to the specified path parameter.
func (c *DefaultCtx) Location(path string) {
c.setCanonical(HeaderLocation, path)

View File

@ -1757,6 +1757,51 @@ func Test_Ctx_Locals(t *testing.T) {
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
}
// go test -run Test_Ctx_Locals_Generic
func Test_Ctx_Locals_Generic(t *testing.T) {
t.Parallel()
app := New()
app.Use(func(c Ctx) error {
Locals[string](c, "john", "doe")
Locals[int](c, "age", 18)
Locals[bool](c, "isHuman", true)
return c.Next()
})
app.Get("/test", func(c Ctx) error {
require.Equal(t, "doe", Locals[string](c, "john"))
require.Equal(t, 18, Locals[int](c, "age"))
require.Equal(t, true, Locals[bool](c, "isHuman"))
require.Equal(t, 0, Locals[int](c, "isHuman"))
return nil
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
}
// go test -run Test_Ctx_Locals_GenericCustomStruct
func Test_Ctx_Locals_GenericCustomStruct(t *testing.T) {
t.Parallel()
type User struct {
name string
age int
}
app := New()
app.Use(func(c Ctx) error {
Locals[User](c, "user", User{"john", 18})
return c.Next()
})
app.Use("/test", func(c Ctx) error {
require.Equal(t, User{"john", 18}, Locals[User](c, "user"))
return nil
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
}
// go test -run Test_Ctx_Method
func Test_Ctx_Method(t *testing.T) {
t.Parallel()

View File

@ -1000,6 +1000,31 @@ app.Get("/admin", func(c fiber.Ctx) error {
})
```
An alternative version of the Locals method that takes advantage of Go's generics feature is also available. This version
allows for the manipulation and retrieval of local values within a request's context with a more specific data type.
```go title="Signature"
func Locals[V any](c Ctx, key any, value ...any) V
```
```go title="Example"
app.Use(func(c Ctx) error {
fiber.Locals[string](c, "john", "doe")
fiber.Locals[int](c, "age", 18)
fiber.Locals[bool](c, "isHuman", true)
return c.Next()
})
app.Get("/test", func(c Ctx) error {
fiber.Locals[string](c, "john") // "doe"
fiber.Locals[int](c, "age") // 18
fiber.Locals[bool](c, "isHuman") // true
return nil
})
````
Make sure to understand and correctly implement the Locals method in both its standard and generic form for better control
over route-specific data within your application.
## Location
Sets the response [Location](https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Location) HTTP header to the specified path parameter.