Go语言编写HTTP Web网络服务有着各种各样的框架和模型,而阅读成熟的实现也是一个好的高效的学习应用途径。

Docker(moby)中对服务的一个实现我认为是比较好的编程范例。

  1. 定义一个通用的Http接口。
// https://github.com/moby/moby/blob/master/api/server/httputils/httputils.go

type APIFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error
  1. 定义路由接口
//  https://github.com/moby/moby/blob/master/api/server/router/router.go

// Router defines an interface to specify a group of routes to add to the docker server.
type Router interface {
    // Routes returns the list of routes to add to the docker server.
    Routes() []Route
}

// Route defines an individual API route in the docker server.
type Route interface {
    // Handler returns the raw function to create the http handler.
    Handler() httputils.APIFunc
    // Method returns the http method that the route responds to.
    Method() string
    // Path returns the subpath where the route responds to.
    Path() string
}
local.go
  1. 定义各种逻辑处理
func  (h *xxhandler) handler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 ......
}
github.com/docker/docker/api/server/routerRouter
  1. 装配

首先需要一个转换函数:

// https://github.com/moby/moby/blob/master/api/server/server.go

func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // Define the context that we'll pass around to share info
        // like the docker-request-id.
        //
        // The 'context' will be used for global data that should
        // apply to all requests. Data that is specific to the
        // immediate function being called should still be passed
        // as 'args' on the function call.
        ctx := context.WithValue(context.Background(), dockerversion.UAStringKey, r.Header.Get("User-Agent"))
        handlerFunc := s.handlerWithGlobalMiddlewares(handler)

        vars := mux.Vars(r)
        if vars == nil {
            vars = make(map[string]string)
        }

        if err := handlerFunc(ctx, w, r, vars); err != nil {
            statusCode := httputils.GetHTTPErrorStatusCode(err)
            if statusCode >= 500 {
                logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
            }
            httputils.MakeErrorHandler(err)(w, r)
        }
    }
}
httputils.APIFunchttp.HandlerFunc
createMuxRoutermux.Router

点评

httputils.APIFunccontexterror