Golang Web 的一个编程范例

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/router 目录下,每个子目录都是一组,而且每组都实现了Router接口。

  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.APIFunc转换为标准http.HandlerFunc,一个妙招。

下来在createMux 这个函数对所有的Router进行遍历,组装为标准的mux.Router,搞定。

点评

这个实现具有一个很好的范例,尤其在httputils.APIFunc上做的非常棒,不仅兼顾了context,还有一个返回的error,可以在处理不同的逻辑时灵活运用。

你可能感兴趣的:(Golang Web 的一个编程范例)