Skip to content

Instantly share code, notes, and snippets.

@msoap
Created July 22, 2021 14:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msoap/4b25842e078658018bb565224fec0ef1 to your computer and use it in GitHub Desktop.
Save msoap/4b25842e078658018bb565224fec0ef1 to your computer and use it in GitHub Desktop.
Handling handlers with different types via Go generics (using go 1.18+)
package main
import (
"encoding/json"
"fmt"
)
func main() {
app := NewApp()
server := NewServer()
server.addHandler("method01", convertHandler(app.Handler01))
server.addHandler("method02", convertHandler(app.Handler02))
out, err := server.run("method01", `{"id": 1}`)
fmt.Printf("Handler01: got resp: %v, err: %v\n", out, err)
out, err = server.run("method01", `{"id": 11}`)
fmt.Printf("Handler01: got resp: %v, err: %v\n", out, err)
out, err = server.run("method02", `{"name": "AAA"}`)
fmt.Printf("Handler02: got resp: %v, err: %v\n", out, err)
out, err = server.run("method02", `{"name": 123}`)
fmt.Printf("Handler02: got resp: %v, err: %v\n", out, err)
}
// app handlers -----------------------
type App struct {
}
func NewApp() *App {
return &App{}
}
type Request01 struct {
ID int `json:"id"`
}
type Response01 struct {
Error string `json:"error"`
}
func (app *App) Handler01(req Request01) (*Response01, error) {
fmt.Printf("Handler01: got req: %v\n", req)
if req.ID > 10 {
return nil, fmt.Errorf("too big ID: %d", req.ID)
}
return &Response01{}, nil
}
type Request02 struct {
Name string `json:"name"`
}
type Response02 struct {
Status int `json:"status"`
}
func (app *App) Handler02(req Request02) (*Response02, error) {
fmt.Printf("Handler02: got req: %+v\n", req)
return &Response02{Status: len(req.Name)}, nil
}
// convert handler to generic -----
func convertHandler[TIN any, TOUT any](fn func(req TIN) (*TOUT, error)) func(payload []byte) ([]byte, error) {
return func(payload []byte) ([]byte, error) {
var req TIN
err := json.Unmarshal(payload, &req)
if err != nil {
return nil, err
}
resp, err := fn(req)
if err != nil {
return nil, err
}
return json.Marshal(*resp)
}
}
// server -------------------------
type Server struct {
handlers map[string]func([]byte) ([]byte, error)
}
func NewServer() *Server {
return &Server{handlers: make(map[string]func([]byte) ([]byte, error))}
}
func (s *Server) addHandler(name string, fn func(payload []byte) ([]byte, error)) {
s.handlers[name] = fn
}
func (s *Server) run(method string, payload string) (string, error) {
if handler, ok := s.handlers[method]; ok {
out, err := handler([]byte(payload))
if err != nil {
return "", err
}
return string(out), nil
}
return "", fmt.Errorf("method %s not found", method)
}
@msoap
Copy link
Author

msoap commented Jul 22, 2021

@msoap
Copy link
Author

msoap commented Aug 27, 2021

with Go 1.17:

go run -gcflags=-G=3 generic_handlers.go

with Go 1.18+:

go run generic_handlers.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment