doujiang2641 2016-10-03 22:30
浏览 43
已采纳

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

I'm designing a API server in Go. I have many database tables, each with a matching struct. Each has a route and handler:

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

The code for Thing2 is pretty much identical, as it is for Thing3 and so on. I will end up with hundreds of things, and therefore lots of duplicated code. It feels like I'm doing something horribly wrong. What's the best way to make this more DRY?

  • 写回答

1条回答 默认 最新

  • drrkgbm6851 2016-10-04 02:33
    关注

    Why not create a factory function for the http.Handler used with each Thing? This allows you to write the showHandler logic once and parameterize the instantiation of individual things.

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

    This function can be used to systematically create routes for a given router. For instance, I can create an explicit registry which keeps track of the path for each thing as well as a ThingFactory instance which is used when calling the createShowHandler factory function.

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

    Naturally, you would want to define interfaces for the various interaction points in a program like this to gain more type safety when dealing with a large number of instances. A more robust registry could be implemented that provided an interface for Things to register themselves with.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 MddBootstrapInitialize2失败
  • ¥15 LCD Flicker
  • ¥15 Spring MVC项目,访问不到相应的控制器方法
  • ¥15 esp32在micropython环境下使用ssl/tls连接mqtt服务器出现以下报错Connected on 192.168.154.223发生意外错误: 5无法连接到 MQTT 代理,如何解决?
  • ¥15 关于#genesiscsheel#的问题,如何解决?
  • ¥15 Android aidl for hal
  • ¥15 STM32CubeIDE下载程序报错
  • ¥15 微信好友如何转变为会员系统?(相关搜索:小程序)
  • ¥15 c# 直接使用c++ 类库文件
  • ¥15 一个主机电脑有两个显示器,当前有两个软件主体是网页html打包的exe程序,如何通过cmd命令bat同时打开软件一个在主屏幕显示,另外一个软件在2副屏幕上显示