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

本文深入剖析GoRestful框架的源码,从容器初始化、路由注册到请求处理流程,详细解读其工作原理及实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

 
 
  • 1
  • 2
  • 3

不用多说这个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)调用具体的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值