一个 WebService 由若干个 Routes 组成,并且 WebService 内的 Routes 拥有同一个 RootPath、输入输出格式、基本一致的请求数据类型等等一系列的通用属性。通常的,我们会根据需要将一组相关性非常强的 API 封装成为一个 WebServiice,继而将 Web Application 所拥有的全部 APIs 划分若干个 Group。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// WebService holds a collection of Route values that bind a Http Method + URL Path to a function.
typeWebServicestruct {
rootPathstringpathExpr*pathExpression// cached compilation of rootPath as RegExp
routes []Routeproduces []stringconsumes []stringpathParameters []*Parameterfilters []FilterFunctiondocumentationstringapiVersionstringtypeNameHandleFuncTypeNameHandleFunctiondynamicRoutesbool// protects 'routes' if dynamic routes are enabled
routesLocksync.RWMutex}
WebService 有一个 Root Path,通过 ws.Path() 方法设置,例如:/users,作为 Group 的 根。
1
2
3
4
5
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
Group 下属的 APIs 都是 RootRoute(RootPath)下属的 SubRoute(SubPath)。每个 Group 就是提供一项服务的 API 集合,每个 Group 会维护一个 Version。Group 的抽象是为了能够安全隔离的对各项服务进行敏捷迭代,当我们对一项服务进行升级时,只需要通过对特定版本号的更新来升级相关的 APIs,而不会影响到整个 Web Server。视实际情况而定,可能是若干个 APIs 分为一个 Group,也有可能一个 API 就是一个 Group。
// Route creates a new Route using the RouteBuilder and add to the ordered list of Routes.
func (w*WebService) Route(builder*RouteBuilder) *WebService {
w.routesLock.Lock()
deferw.routesLock.Unlock()
builder.copyDefaults(w.produces, w.consumes)
w.routes = append(w.routes, builder.Build())
returnw}
// GET is a shortcut for .Method("GET").Path(subPath)
func (w*WebService) GET(subPathstring) *RouteBuilder {
return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("GET").Path(subPath)
}
func (b*RouteBuilder) servicePath(pathstring) *RouteBuilder {
b.rootPath = pathreturnb}
// Method specifies what HTTP method to match. Required.
func (b*RouteBuilder) Method(methodstring) *RouteBuilder {
b.httpMethod = methodreturnb}
// Path specifies the relative (w.r.t WebService root path) URL path to match. Default is "/".
func (b*RouteBuilder) Path(subPathstring) *RouteBuilder {
b.currentPath = subPathreturnb}
// To bind the route to a function.
// If this route is matched with the incoming Http Request then call this function with the *Request,*Response pair. Required.
func (b*RouteBuilder) To(functionRouteFunction) *RouteBuilder {
b.function = functionreturnb}
packagemainimport (
"io""log""net/http"restful"github.com/emicklei/go-restful")
// This example shows the minimal code needed to get a restful.WebService working.
//
// GET http://localhost:8080/hello
funcmain() {
ws:= new(restful.WebService)
ws.Route(ws.GET("/hello").To(hello))
restful.Add(ws)
log.Fatal(http.ListenAndServe(":8080", nil))
}
funchello(req*restful.Request, resp*restful.Response) {
io.WriteString(resp, "hello world from houmin\n")
}
上面的代码比较简单,包含一个 hello 的 Handler,通过 ws.Route(ws.GET("/hello").To(hello)) 将其注册到 WebService,然后启动了一个 WebServer,就可以了通过 GET 方法访问了,如下所示:
上一小节虽然 Work 了,但是还是有一个问题,我们通过 Go 自带的 net/http 包启动的 WebServer 是如何和我们定义的 WebService 联系起来的呢?在解答这个问题之前,我们首先来了解下 Container。
Container 表示一个 Web Server,由多个 WebServices 组成,此外还包含了若干个 Filters、一个 http.ServeMux 多路复用器以及一个 dispatch。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
// The requests are further dispatched to routes of WebServices using a RouteSelector
typeContainerstruct {
webServicesLocksync.RWMutexwebServices []*WebServiceServeMux*http.ServeMuxisRegisteredOnRootboolcontainerFilters []FilterFunctiondoNotRecoverbool// default is true
recoverHandleFuncRecoverHandleFunctionserviceErrorHandleFuncServiceErrorHandleFunctionrouterRouteSelector// default is a CurlyRouter (RouterJSR311 is a slower alternative)
contentEncodingEnabledbool// default is false
}
// HandleFunc registers the handler function for the given pattern.
func (mux*ServeMux) HandleFunc(patternstring, handlerfunc(ResponseWriter, *Request)) {
ifhandler==nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
// addHandler may set a new HandleFunc for the serveMux
func (c*Container) addHandler(service*WebService, serveMux*http.ServeMux) bool {
// ...
if !alreadyMapped {
serveMux.HandleFunc(pattern, c.dispatch)
if !strings.HasSuffix(pattern, "/") {
serveMux.HandleFunc(pattern+"/", c.dispatch)
}
}
returnfalse}
那么 addHandler 是在哪里被调用的呢?这就是我们的 Add 函数
1
2
3
4
5
6
7
8
9
10
11
// Add a WebService to the Container. It will detect duplicate root paths and exit in that case.
func (c*Container) Add(service*WebService) *Container {
// ...
// 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)
returnc}
// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
func (c*Container) ServeHTTP(httpWriterhttp.ResponseWriter, httpRequest*http.Request) {
// ...
c.ServeMux.ServeHTTP(writer, httpRequest)
}
// DefaultContainer is a restful.Container that uses http.DefaultServeMux
varDefaultContainer*Containerfuncinit() {
DefaultContainer = NewContainer()
DefaultContainer.ServeMux = http.DefaultServeMux}
// Add registers a new WebService add it to the DefaultContainer.
funcAdd(service*WebService) {
DefaultContainer.Add(service)
}
typeFilterFunctionfunc(*Request, *Response, *FilterChain)
typeFilterChainstruct {
Filters []FilterFunction// ordered list of FilterFunction
Indexint// index into filters that is currently in progress
TargetRouteFunction// function to call after passing all filters
}
// Filter appends a container FilterFunction. These are called before dispatching
// a http.Request to a WebService from the container
func (c*Container) Filter(filterFilterFunction) {
c.containerFilters = append(c.containerFilters, filter)
}
// Filter adds a filter function to the chain of filters applicable to all its Routes
func (w*WebService) Filter(filterFilterFunction) *WebService {
w.filters = append(w.filters, filter)
returnw}
// Filter appends a FilterFunction to the end of filters for this Route to build.
func (b*RouteBuilder) Filter(filterFilterFunction) *RouteBuilder {
b.filters = append(b.filters, filter)
returnb}