2016-10-03 55 views
3

我在Go设计一个API服务器。我有许多数据库表,每个表都有一个匹配的struct。每个人都有一个路由和处理程序:如何减少golang中重复的http处理程序代码?

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) 
} 

Thing2的代码几乎是相同的,因为它是为Thing3等等。我最终会得到成百上千的东西,因此很多重复的代码。感觉我正在做一些可怕的错误。什么是使这个更干的最好方法?

回答

3

为什么不为每个Thing使用的http.Handler创建工厂功能?这允许您编写一次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) 
    } 
} 

该函数可用于系统地为给定路由器创建路由。例如,我可以创建一个显式注册表,用于跟踪每个事物的路径以及调用工厂函数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") 
} 

当然,你想在这样的程序有大量实例打交道时获得更多的类型安全定义的各种交互点接口。可以实施更强大的注册表,为Thing提供注册自己的界面。

+0

谢谢!我会试一试。 – Pippin