綜述
調(diào)試樣例為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")
在之前的樣例中晴竞,都沒有設(shè)置過rootpath,restful中通過調(diào)用Path函數(shù)狠半,改變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
}
在該函數(shù)中首先將web service的root path設(shè)置為傳入的/user, 并且將調(diào)用compilePathExpression
生成對應(yīng)的的path匹配規(guī)則
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進行加工神年,產(chǎn)出expression=^/users(/.*)?$
已维、token=/hello
,并創(chuàng)建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各是一組屬性垛耳,通過對應(yīng)的接口可以直接給對應(yīng)的屬性賦值
// 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匹配過程中,當(dāng)http訪問中飘千,在獲取route的函數(shù)中detectRoute
for _, each := range methodOk {
if each.matchesContentType(contentType) {
inputMediaOk = append(inputMediaOk, each)
}
}
會執(zhí)行matchesContentType艾扮,在這個函數(shù)中,將會從ws中獲取到consumer檢查用戶的http content type是否符合consumer的設(shè)置占婉,而produces的生效是用戶調(diào)用WriteEntity函數(shù)時泡嘴,調(diào)用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
}