Created
July 22, 2021 14:59
-
-
Save msoap/4b25842e078658018bb565224fec0ef1 to your computer and use it in GitHub Desktop.
Handling handlers with different types via Go generics (using go 1.18+)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
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
run: https://go2goplay.golang.org/p/gcy2vqJJVMZ