Custom 🔌
You can extend httpin by adding your custom directives.
Firstly you should understand the concepts of Directive and Directive Executor.
To add your custom directive, you need to:
Steps
1. Create a Directive Executor
by implementing the httpin_core.DirectiveExecutor
interface:
type DirectiveCaseFormatter struct {
Transform func(string) string
}
func (f *DirectiveCaseFormatter) Decode(rtm *httpin_core.DirectiveRuntime) error {
if rtm.Value.Type().Elem().Kind() != reflect.String {
return errors.New("not a string")
}
currentValue := rtm.Value.Elem().String()
newValue := f.Transform(currentValue)
rtm.Value.Elem().SetString(newValue)
return nil
}
func (f *DirectiveCaseFormatter) Encode(rtm *httpin_core.DirectiveRuntime) error {
if rtm.Value.Type().Kind() != reflect.String {
return errors.New("not a string")
}
currentValue := rtm.Value.String()
newValue := f.Transform(currentValue)
rtm.Value.SetString(newValue)
return nil
}
2. Register your Directive to httpin
by calling httpin_core.RegisterDirective
:
func init() {
httpin_core.RegisterDirective("to_lowercase", DirectiveCaseFormatter{
Transform: strings.ToLower,
})
httpin_core.RegisterDirective("to_uppercase", DirectiveCaseFormatter{
Transform: strings.ToUpper,
})
}
NB: make sure to register your directive executor before you start the server.
3. Use your Directive
type ListUsersInput struct {
Username string `in:"query=username;to_lowercase"`
Gender string `in:"query=gender;to_uppercase"`
}
Runable Example
package main
import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"github.com/ggicci/httpin"
httpin_core "github.com/ggicci/httpin/core"
"github.com/go-chi/chi/v5"
)
type DirectiveCaseFormatter struct {
Transform func(string) string
}
func (f *DirectiveCaseFormatter) Decode(rtm *httpin_core.DirectiveRuntime) error {
if rtm.Value.Type().Elem().Kind() != reflect.String {
return errors.New("not a string")
}
currentValue := rtm.Value.Elem().String()
newValue := f.Transform(currentValue)
rtm.Value.Elem().SetString(newValue)
return nil
}
func (f *DirectiveCaseFormatter) Encode(rtm *httpin_core.DirectiveRuntime) error {
if rtm.Value.Type().Kind() != reflect.String {
return errors.New("not a string")
}
currentValue := rtm.Value.String()
newValue := f.Transform(currentValue)
rtm.Value.SetString(newValue)
return nil
}
func init() {
httpin_core.RegisterDirective("to_lowercase", &DirectiveCaseFormatter{
Transform: strings.ToLower,
})
httpin_core.RegisterDirective("to_uppercase", &DirectiveCaseFormatter{
Transform: strings.ToUpper,
})
}
type ListUsersInput struct {
Username string `in:"query=username;to_lowercase"`
Gender string `in:"query=gender;to_uppercase"`
}
func ListUsersHandler(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*ListUsersInput)
fmt.Printf("input: %#v\n", input)
}
func main() {
router := chi.NewRouter()
// Bind input struct with handler.
router.With(
httpin.NewInput(ListUsersInput{}),
).Get("/users", ListUsersHandler)
r, _ := http.NewRequest("GET", "/users?username=Ggicci&gender=male", nil)
rw := httptest.NewRecorder()
router.ServeHTTP(rw, r)
}