K8s api-server 的接口都是使用了 restful-api 风格, Web框架用的是go-resful 。
什么是 REST?
REST Resource Representational State Transfer(资源在网络中以某种表现形式进行状态转移):
- Resource:资源,就是数据,比如:用户,商品等;
- Representational:某种表现形式,比如用JSON,XML,JPEG等;
- State Transfer:状态变化。通过HTTP动词实现。
go-restful 官网解释
REST 要求开发人员以与协议定义一致的方式使用 HTTP 方法。
这种基本的 REST 设计原则在创建、读取、更新和删除 (CRUD) 操作与 HTTP 方法之间建立了一对一的映射。
- GET = 查找资源
- POST = 创建资源
- PUT = 如果发送指定资源 (URI) 的全部内容,则创建
- PUT = 如果更新指定资源的全部内容,则更新
- DELETE = 请求服务器删除资源
- PATCH = 更新资源的部分内容
- OPTIONS = 获取有关请求 URI 的通信选项的信息
基本使用
// 创建 WebService
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML)
// ws 添加 Route
ws.Route(ws.GET("/{user-id}").To(u.findUser).
Doc("get a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
Writes(User{}))
...
// 定义findUser handle
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
...
}
// 创建 Container
container := restful.NewContainer()
// 注册ws 到 Container
container.add(ws)
// 注册到8081 端口
server := &http.Server{Addr: ":8081", Handler: container}
主要有3个类:
- Container: 可以包含多个WebService,将一个WebService集合注册到一个端口
- WebService: 可以包含多个 Route,一般一个对象的增删改查(CRUD)为一个WebService
- Route:HTTP 方法、URL 路径和它使用的MIME 类型 定义,MIME 类型可由 消费接口(Consumes)和生产接口(Produces)按需转换
例子:
当客户端需要查询 user id 为 1的用户时,需要发送一个GET(PATH=/users/1)的请求到服务器的8081端口
curl -X GET http://{domain}:8081/users/1
服务接收到请求后,container dispatch 请求,先选择最匹配的WebService 和 Route( 如果对匹配算法不满意,可以自己定义好 router,替换掉 Container的 router)
webService, route, err = c.router.SelectRoute(
c.webServices,
httpRequest)
解析url中的参数:user_id=1
pathParams := pathProcessor.ExtractParameters(route, webService, httpRequest.URL.Path)
再包装一个request和response,将参数带入route
wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest, pathParams)
func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request, pathParams map[string]string) (*Request, *Response) {
wrappedRequest := NewRequest(httpRequest)
wrappedRequest.pathParameters = pathParams
wrappedRequest.selectedRoutePath = r.Path
wrappedResponse := NewResponse(httpWriter)
wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
wrappedResponse.routeProduces = r.Produces
return wrappedRequest, wrappedResponse
}
执行FilterFunction(过滤链),如果有的话,先执行 container的过滤链,一般为权限认证之类的,再执行 webService 和 route 的过滤链。
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)
}
最后执行route 的handle:findUser函数,把id为1的用户信息写到response 发送给客户端,这是大概的流程,省略了一些细节。
Restful 的好处
- 保证 HEAD 和 GET 方法是安全的,不会对资源状态有所改变(污染)
- url统一风格,保持简洁:URI使用名词而不是动词,且推荐用复数; 比如 /deleteUser?id=1, 这种写法不好,推荐使用 。/user/user_id 请求方法为DELETE:curl -X DELETE {domain}/user/1
- 在返回结果用明确易懂的文本