我正在用 Go 设计一个 API 服务器。我有很多数据库表,每个表都有一个匹配的struct. 每个都有一个路线和处理程序:
type Thing1 struct {
ID int64
Name string
...
}
func main() {
...
router := mux.NewRouter()
apiRouter := router.PathPrefix("/v1").Subrouter()
apiRouter.HandleFunc("/thing1/{id}", Thing1ShowHandler).Methods("GET")
}
func Thing1ShowHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.ParseInt(vars["id"], 10, 64)
if err != nil {
errorHandler(w, err)
return
}
thing1 := Thing1{ID: id}
err = db.First(&thing1, id).Error
if thing1.ID > 0 {
jsonHeaders(w, http.StatusOK)
if err := json.NewEncoder(w).Encode(thing1); err != nil {
errorHandler(w, err)
}
return
}
notFoundHandler(w, r)
}
Run Code Online (Sandbox Code Playgroud)
for 的代码Thing2几乎是相同的,因为它是 forThing3等等。我最终会得到数百个东西,因此会有很多重复的代码。感觉就像我正在做一些非常错误的事情。让这个更干燥的最好方法是什么?
http.Handler为什么不为每个使用创建一个工厂函数Thing?这允许您编写showHandler一次逻辑并参数化各个事物的实例化。
// A ThingFactory returns a Thing struct configured with the given ID.
type ThingFactory func(id int64) interface{}
// The createShowHandler function is a factory function for creating a handler
// which uses the getThing factory function to obtain an instance of a
// thing to use when generating a view.
func createShowHandler(getThing ThingFactory) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.ParseInt(vars["id"], 10, 64)
if err != nil {
errorHandler(w, err)
return
}
thing := getThing(id)
err = db.First(&thing, id).Error
if err != nil {
errorHandler(w, err)
}
if thing1.ID > 0 {
jsonHeaders(w, http.StatusOK)
if err := json.NewEncoder(w).Encode(thing1); err != nil {
errorHandler(w, err)
}
return
}
notFoundHandler(w, r)
}
}
Run Code Online (Sandbox Code Playgroud)
此功能可用于系统地为给定路由器创建路由。例如,我可以创建一个显式注册表,用于跟踪每个事物的路径以及ThingFactory调用工厂函数时使用的实例createShowHandler。
router := mux.NewRouter()
apiRouter := router.PathPrefix("/v1").Subrouter()
registry := []struct {
path string
handler ThingFactory
}{
{"/thing1/{id}", func(id int64) interface{} { return Thing1{ID: id} }},
{"/thing2/{id}", func(id int64) interface{} { return Thing2{ID: id} }},
{"/thing3/{id}", func(id int64) interface{} { return Thing3{ID: id} }},
}
for _, registrant := range registry {
apiRouter.HandleFunc(registrant.path, createShowHandler(registrant.handler)).Methods("GET")
}
Run Code Online (Sandbox Code Playgroud)
当然,您会希望为这样的程序中的各个交互点定义接口,以便在处理大量实例时获得更多的类型安全性。可以实现一个更强大的注册表,为Things 提供注册自己的接口。