如何在golang中减少重复的http处理程序代码?

时间:2016-10-03 22:30:11

标签: go

我正在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)
}

Thing2的代码与Thing3的代码完全相同,依此类推。我最终会得到数百个东西,因此会有大量重复的代码。感觉我正在做一些可怕的错误。什么是使这更干燥的最好方法?

1 个答案:

答案 0 :(得分:3)

为什么不为每个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)
    }
}

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

当然,您需要为此类程序中的各种交互点定义接口,以便在处理大量实例时获得更多类型安全性。可以实现更强大的注册表,为Thing s注册自己提供了一个接口。