go restful源码剖析-4

综述


调试样例为examples\restful-encoding-filter.go,在该例子中主要引入了Path、Comsumer、Produces的概念,代码如下。

func main() {
    restful.Add(NewUserService())
    log.Print("start listening on localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func NewUserService() *restful.WebService {
    ws := new(restful.WebService)
    ws.
        Path("/users").
        Consumes(restful.MIME_XML, restful.MIME_JSON).
        Produces(restful.MIME_JSON, restful.MIME_XML)

    // install a response encoding filter
    ws.Route(ws.GET("/{user-id}").Filter(encodingFilter).To(findUser))
    return ws
}

// Route Filter (defines FilterFunction)
func encodingFilter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
    log.Printf("[encoding-filter] %s,%s\n", req.Request.Method, req.Request.URL)
    // wrap responseWriter into a compressing one
    compress, _ := restful.NewCompressingResponseWriter(resp.ResponseWriter, restful.ENCODING_GZIP)
    resp.ResponseWriter = compress
    defer func() {
        compress.Close()
    }()
    chain.ProcessFilter(req, resp)
}

// GET http://localhost:8080/users/42
//
func findUser(request *restful.Request, response *restful.Response) {
    log.Print("findUser")
    response.WriteEntity(User{"42", "Gandalf"})
}

Path添加流程


ws.Path("/users")在之前的样例中,都没有设置过rootpath,restful中通过调用Path函数,改变web service中的root path。

func (w *WebService) Path(root string) *WebService {
    w.rootPath = root
    if len(w.rootPath) == 0 {
        w.rootPath = "/"
    }
    w.compilePathExpression()
    return w
}

在该函数中首先将web service的root path设置为传入的/user, 并且将调用compilePathExpression生成对应的的path匹配规则

func (w *WebService) compilePathExpression() {
    compiled, err := newPathExpression(w.rootPath)
    if err != nil {
        log.Printf("invalid path:%s because:%v", w.rootPath, err)
        os.Exit(1)
    }
    w.pathExpr = compiled
}
func newPathExpression(path string) (*pathExpression, error) {
    expression, literalCount, varNames, varCount, tokens := templateToRegularExpression(path)
    compiled, err := regexp.Compile(expression)
    if err != nil {
        return nil, err
    }
    return &pathExpression{literalCount, varNames, varCount, compiled, expression, tokens}, nil
}

在处理过程中,对web service的rootpath进行加工,产出expression=^/users(/.*)?$token=/hello,并创建pathExpression对象返回。

type pathExpression struct {
    LiteralCount int      // the number of literal characters (means those not resulting from template variable substitution)
    VarNames     []string // the names of parameters (enclosed by {}) in the path
    VarCount     int      // the number of named parameters (enclosed by {}) in the path
    Matcher      *regexp.Regexp
    Source       string // Path as defined by the RouteBuilder
    tokens       []string
}

[图片上传失败...(image-306dc5-1535615090387)]

consumer及producer赋值及生效


在web service中consumer及producer各是一组属性,通过对应的接口可以直接给对应的属性赋值

// Produces specifies that this WebService can produce one or more MIME types.
// Http requests must have one of these values set for the Accept header.
func (w *WebService) Produces(contentTypes ...string) *WebService {
    w.produces = contentTypes
    return w
}

// Consumes specifies that this WebService can consume one or more MIME types.
// Http requests must have one of these values set for the Content-Type header.
func (w *WebService) Consumes(accepts ...string) *WebService {
    w.consumes = accepts
    return w
}

最终consumes生效在进行route匹配过程中,当http访问中,在获取route的函数中detectRoute

    for _, each := range methodOk {
        if each.matchesContentType(contentType) {
            inputMediaOk = append(inputMediaOk, each)
        }
    }

会执行matchesContentType,在这个函数中,将会从ws中获取到consumer检查用户的http content type是否符合consumer的设置,而produces的生效是用户调用WriteEntity函数时,调用EntityWriter使用。

// WriteEntity calls WriteHeaderAndEntity with Http Status OK (200)
func (r *Response) WriteEntity(value interface{}) error {
    return r.WriteHeaderAndEntity(http.StatusOK, value)
}
func (r *Response) EntityWriter() (EntityReaderWriter, bool) {
    sorted := sortedMimes(r.requestAccept)
    for _, eachAccept := range sorted {
        for _, eachProduce := range r.routeProduces {
            if eachProduce == eachAccept.media {
                if w, ok := entityAccessRegistry.accessorAt(eachAccept.media); ok {
                    return w, true
                }
            }
        }
        if eachAccept.media == "*/*" {
            for _, each := range r.routeProduces {
                if w, ok := entityAccessRegistry.accessorAt(each); ok {
                    return w, true
                }
            }
        }
    }
    // if requestAccept is empty
    writer, ok := entityAccessRegistry.accessorAt(r.requestAccept)
    if !ok {
        // if not registered then fallback to the defaults (if set)
        if DefaultResponseMimeType == MIME_JSON {
            return entityAccessRegistry.accessorAt(MIME_JSON)
        }
        if DefaultResponseMimeType == MIME_XML {
            return entityAccessRegistry.accessorAt(MIME_XML)
        }
        // Fallback to whatever the route says it can produce.
        // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
        for _, each := range r.routeProduces {
            if w, ok := entityAccessRegistry.accessorAt(each); ok {
                return w, true
            }
        }
        if trace {
            traceLogger.Printf("no registered EntityReaderWriter found for %s", r.requestAccept)
        }
    }
    return writer, ok
}

你可能感兴趣的:(go restful源码剖析-4)