go-restful实战与深入分析之源码篇

来源:互联网 发布:excel如何将数据分组 编辑:程序博客网 时间:2024/04/30 20:57

上一篇分析了go 原生的http服务。下面开始介绍go restful源码分析,有了上一篇的铺垫这篇文章讲解起来就很简单了。和go 的http一样启动监听:

    wsContainer := restful.NewContainer()    server := &http.Server{Addr: ":8080", Handler: wsContainer}

不用多说这个wsContainer肯定也是实现了Handler接口的。看代码:

func NewContainer() *Container {    return &Container{        webServices:            []*WebService{},        ServeMux:               http.NewServeMux(),        isRegisteredOnRoot:     false,        containerFilters:       []FilterFunction{},        doNotRecover:           true,        recoverHandleFunc:      logStackOnRecover,        serviceErrorHandleFunc: writeServiceError,        router:                 CurlyRouter{},        contentEncodingEnabled: false}}func (c *Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {    c.ServeMux.ServeHTTP(httpwriter, httpRequest)}

上面有两个方式分别是定义container和实现Handler接口的的ServeHTTP()方法。那么当服务请求过来后就可以调用这个方法了。这个方法又调用c.ServeMux.ServeHTTP,是不是回到上一篇了,对,就练到一起了。

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {    if r.RequestURI == "*" {        if r.ProtoAtLeast(1, 1) {            w.Header().Set("Connection", "close")        }        w.WriteHeader(StatusBadRequest)        return    }    h, _ := mux.Handler(r)    h.ServeHTTP(w, r)}

那么先看他是怎么注册和查找方法的呢?

    ws := new(restful.WebService)    ws.    Path("/users").        Consumes(restful.MIME_XML, restful.MIME_JSON).        Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well    ws.Route(ws.GET("/{user-id}").To(u.findUser))    ws.Route(ws.POST("").To(u.updateUser))    ws.Route(ws.PUT("/{user-id}").To(u.createUser))    ws.Route(ws.DELETE("/{user-id}").To(u.removeUser))    container.Add(ws)

先看看Route方法,这里面记录所以路径和路径对应的方法,全部放到[]Route这个切片当中。

func (w *WebService) Route(builder *RouteBuilder) *WebService {    w.routesLock.Lock()    defer w.routesLock.Unlock()    builder.copyDefaults(w.produces, w.consumes)    w.routes = append(w.routes, builder.Build())    return w}

一个container里面多个webservice。可以通过Container的Add注册加入到webServices这个切片中。

func (c *Container) Add(service *WebService) *Container {    c.webServicesLock.Lock()    defer c.webServicesLock.Unlock()    // if rootPath was not set then lazy initialize it    if len(service.rootPath) == 0 {        service.Path("/")    }    // cannot have duplicate root paths    for _, each := range c.webServices {        if each.RootPath() == service.RootPath() {            log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)            os.Exit(1)        }    }    // If not registered on root then add specific mapping    if !c.isRegisteredOnRoot {        c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux)    }    c.webServices = append(c.webServices, service)    return c}

注册webservice的本质是注册各种handler看addHandler这个方法。

func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) bool {    pattern := fixedPrefixPath(service.RootPath())    // check if root path registration is needed    if "/" == pattern || "" == pattern {        serveMux.HandleFunc("/", c.dispatch)        return true    }    // detect if registration already exists    alreadyMapped := false    for _, each := range c.webServices {        if each.RootPath() == service.RootPath() {            alreadyMapped = true            break        }    }    if !alreadyMapped {        serveMux.HandleFunc(pattern, c.dispatch)        if !strings.HasSuffix(pattern, "/") {            serveMux.HandleFunc(pattern+"/", c.dispatch)        }    }    return false}

通过serveMux.HandleFunc注册,这也和上一篇的文章对应。不过这次注册的方法c.dispatch有点特别,具体看下面的方法,

func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {    writer := httpWriter    // CompressingResponseWriter should be closed after all operations are done    defer func() {        if compressWriter, ok := writer.(*CompressingResponseWriter); ok {            compressWriter.Close()        }    }()    // Instal panic recovery unless told otherwise    if !c.doNotRecover { // catch all for 500 response        defer func() {            if r := recover(); r != nil {                c.recoverHandleFunc(r, writer)                return            }        }()    }    // Install closing the request body (if any)    defer func() {        if nil != httpRequest.Body {            httpRequest.Body.Close()        }    }()    // Detect if compression is needed    // assume without compression, test for override    if c.contentEncodingEnabled {        doCompress, encoding := wantsCompressedResponse(httpRequest)        if doCompress {            var err error            writer, err = NewCompressingResponseWriter(httpWriter, encoding)            if err != nil {                log.Print("[restful] unable to install compressor: ", err)                httpWriter.WriteHeader(http.StatusInternalServerError)                return            }        }    }    // Find best match Route ; err is non nil if no match was found    var webService *WebService    var route *Route    var err error    func() {        c.webServicesLock.RLock()        defer c.webServicesLock.RUnlock()        webService, route, err = c.router.SelectRoute(            c.webServices,            httpRequest)    }()    if err != nil {        // a non-200 response has already been written        // run container filters anyway ; they should not touch the response...        chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {            switch err.(type) {            case ServiceError:                ser := err.(ServiceError)                c.serviceErrorHandleFunc(ser, req, resp)            }            // TODO        }}        chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))        return    }    wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest)    // pass through filters (if any)    if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {        // compose filter chain        allFilters := []FilterFunction{}        allFilters = append(allFilters, c.containerFilters...)        allFilters = append(allFilters, webService.filters...)        allFilters = append(allFilters, route.Filters...)        chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {            // handle request by route after passing all filters            route.Function(wrappedRequest, wrappedResponse)        }}        chain.ProcessFilter(wrappedRequest, wrappedResponse)    } else {        // no filters, handle request by route        route.Function(wrappedRequest, wrappedResponse)    }}

这个里面方法里面通过SelectRoute选取对应的webservice和route,具体实现在:

func (c CurlyRouter) SelectRoute(    webServices []*WebService,    httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) {    requestTokens := tokenizePath(httpRequest.URL.Path)    detectedService := c.detectWebService(requestTokens, webServices)    if detectedService == nil {        if trace {            traceLogger.Printf("no WebService was found to match URL path:%s\n", httpRequest.URL.Path)        }        return nil, nil, NewError(http.StatusNotFound, "404: Page Not Found")    }    candidateRoutes := c.selectRoutes(detectedService, requestTokens)    if len(candidateRoutes) == 0 {        if trace {            traceLogger.Printf("no Route in WebService with path %s was found to match URL path:%s\n", detectedService.rootPath, httpRequest.URL.Path)        }        return detectedService, nil, NewError(http.StatusNotFound, "404: Page Not Found")    }    selectedRoute, err := c.detectRoute(candidateRoutes, httpRequest)    if selectedRoute == nil {        return detectedService, nil, err    }    return detectedService, selectedRoute, nil}

然后通过route.Function(wrappedRequest, wrappedResponse)调用具体的方法。

0 0
原创粉丝点击