目录

文章目录

  • 目录
  • 一个 RESTful API 框架需要什么?
  • go-restful
    • 核心概念
      • Route
      • WebService
      • Container
    • 过滤器(Filter)
    • 响应编码(Response Encoding)
  • 代码示例一
  • 代码示例二

一个 RESTful API 框架需要什么?

从应用程序开发的角度来看,RESTful API 的本质是一个 Web Application,而 RESTful API 框架就是实现这个 Web Application 所封装的一些列工具库,使开发者可以忽略底层实现的复杂度,专注以自身 Application 的逻辑设计。

一个 RESTful API 框架应该具备以下几个元素:

  • Resources:资源的定义,即 HTTP URI(或称之为 HTTP URL Path)的定义。RESTful API 的设计围绕着 Resource 进行建模。
  • Handlers:资源处理器,是资源业务逻辑处理的具体实现。
  • Request Routers:资源请求路由器,完成 HTTP URIs、HTTP Request Methods 和 Handlers 三者之间的映射与路由。
  • Request Verification Schemas:HTTP Request Body 校验器,验证请求实体的合法性。
  • Response View Builder:HTTP Response Body 生成器,生成合法的响应实体。
  • Controllers:资源表现层状态转移控制器,每个 Resource 都有着各自的 Controller,将 Resource 自身及其所拥有的 Handlers、Request Verification Schemas 以及 Response View Builder 进行封装,配合 Request Routers 完成 RESTful 请求的处理即响应。

go-restful

go-restful 是一个 Golang 第三方库,是一个轻量的 RESTful API 框架,基于 Golang Build-in 的 http/net 库。适用于构建灵活多变的 Web Application,Kubernetes 的 ApiServer 也使用了 go-restful。

  • Github:https://github.com/emicklei/go-restful
  • Doc:https://godoc.org/github.com/emicklei/go-restful

go-restful 具有以下特性

  • 支持可配置的请求路由,默认使用 CurlyRouter 快速路由算法,也支持 RouterJSR311。
  • 支持在 URL path 上定义正则表达式,例如:/static/{subpath:*}
  • 提供 Request API 用于从 JSON、XML 读取路径参数、查询参数、头部参数,并转换为 Struct。
  • 提供 Response API 用于将 Struct 写入到 JSON、XML 以及 Header。
  • 支持在服务级、或路由级对请求、响应流进行过滤和拦截。
  • 支持使用过滤器自动响应 OPTIONS 请求和 CORS(跨域)请求。
  • 支持使用 RecoverHandler 自定义处理 HTTP 500 错误。
  • 支持使用 ServiceErrorHandler 自定义处理路由错误产生 HTTP 404/405/406/415 等错误。
  • 支持对请求、响应的有效负载进行编码(例如:gzip、deflate)。
  • 支持使用 CompressorProvider 注册自定义的 gzip、deflate 的读入器和输出器。
  • 支持使用 EntityReaderWriter 注册的自定义编码实现。
  • 支持 Swagger UI 编写的 API 文档。
  • 支持可配置的日志跟踪。

核心概念

Route

Route 表示一条请求路由记录,即:Resource 的 URL Path(URI),从编程的角度可细分为 RootPath 和 SubPath。Route 包含了 Resource 的 URL Path、HTTP Method、Handler 三者之间的组合映射关系。go-restful 内置的 RouteSelector(请求路由分发器)根据 Route 将客户端发出的 HTTP 请求路由到相应的 Handler 进行处理。

go-restful 支持两种路由分发器:快速路由 CurlyRouter 和 RouterJSR311。实际上,CurlyRoute 也是基于 RouterJSR311 的,相比 RouterJSR11,还支持了正则表达式和动态参数,也更加轻量级,Kubernetes ApiServer 中使用的就是这种路由。

CurlyRouter 的元素包括:请求路径(URL Path),请求参数(Parameter),输入、输出类型(Writes、Reads Model),处理函数(Handler),响应内容类型(Accept)等。

WebService

一个 WebService 由若干个 Routes 组成,并且 WebService 内的 Routes 拥有同一个 RootPath、输入输出格式、基本一致的请求数据类型等等一系列的通用属性。通常的,我们会根据需要将一组相关性非常强的 API 封装成为一个 WebServiice,继而将 Web Application 所拥有的全部 APIs 划分若干个 Group。

所以,WebService 至少会有一个 Root Path,通过 ws.Path() 方法设置,例如:/user_group,作为 Group 的 “根”。Group 下属的 APIs 都是 RootRoute(RootPath)下属的 SubRoute(SubPath)。

每个 Group 就是提供一项服务的 API 集合,每个 Group 会维护一个 Version。Group 的抽象是为了能够安全隔离的对各项服务进行敏捷迭代,当我们对一项服务进行升级时,只需要通过对特定版本号的更新来升级相关的 APIs,而不会影响到整个 Web Server。视实际情况而定,可能是若干个 APIs 分为一个 Group,也有可能一个 API 就是一个 Group。

Container

Container 表示一个 Web Server(服务器),由多个 WebServices 组成,此外还包含了若干个 Filters(过滤器)、一个 http.ServeMux 多路复用器以及一个 dispatch。go-restful 如何在从 Container 开始将路由分发给各个 WebService,再由 WebService 分发给具体的 Handler 函数,这些都在 dispatch 中实现。

开发者根据需要创建 Container 实例之后,将 Container 加载到一个 http.Server 上运行。示例:

// 构建一个 WebService 实例。
ws := new(restful.WebService)// 定义 Root Path。
ws.Path("/users")// 定义一个 WebService 下属的 Route。
ws.Route(ws.GET("/users").To(u.findAllUsers).Doc("get all users").Metadata(restfulspec.KeyOpenAPITags, tags).Writes([]User{}).Returns(200, "OK", []User{}))// 构建一个 Container 实例。
container := restful.NewContainer()// 将 Container 加载到 http.Server 运行。
server := &http.Server{Addr: ":8081", Handler: container}

过滤器(Filter)

go-restful 支持服务级、路由级的请求或响应过滤。开发者可以使用 Filter 来执行常规的日志记录、计量、验证、重定向、设置响应头部等工作。go-restful 提供了 3 个针对请求、响应的钩子(Hooks),此外,还可以实现自定义的 Filter。

每个 Filter 必须实现一个 FilterFunction:

func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain)

并使用如下语句传递请求、响应对到下一个 Filter 或 RouteFunction:

chain.ProcessFilter(req, resp)
  • Container Filter:在注册 WebService 之前处理。
// 安装一个全局的 Filter 到 Default Container
restful.Filter(globalLogging)
  • WebService Filter:路由 WebService 之前处理。
// 安装一个 WebService Filter
ws.Filter(webserviceLogging).Filter(measureTime)
  • Route Filter:在调用 Router 相关的函数之前处理。
// 安装 2 个链式的 Route Filter
ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter))
  • OPTIONS Filter:使 WebService 可以响应 HTTP OPTIONS 请求。
Filter(OPTIONSFilter())
  • CORS Filter:是 WebService 可以响应 CORS 请求。
cors := CrossOriginResourceSharing{ExposeHeaders: []string{"X-My-Header"},CookiesAllowed: false,Container: DefaultContainer
}Filter(cors.Filter)

响应编码(Response Encoding)

如果 HTTP Request 包含了 Accept-Encoding Header,那么 HTTP Response 就必须使用指定的编码格式进行压缩。go-restful 目前支持 zip 、deflate 这两种响应编码格式。

如果要为所有的响应启用它们:

restful.DefaultContainer.EnableContentEncoding(true)

同时,也可以通过创建一个 Filter 来实现自定义的响应编码过滤器,并将其安装到每一个 WebService 和 Route 上。

代码示例一

下述示例实现了对 users 资源的 CURD API。实现的过程如下:

  1. 定义 User resource。
  2. 定义 User 的 Handlers。
  3. 定义一个 User resource Register(资源注册器)
  4. 在 User resource Register 内构造了 WebService 实例、定义了 User 的 URL RootPath /users、以及多个 SubPath 的 Routes。并且 HTTP Method、User Path(RootPath + SubPath)、Handlers 在 Routes 内建立映射关系。最后将 Routes 关联到 WebService、将 WebServices 关联到 Container。
package mainimport ("log""net/http""github.com/emicklei/go-restful"
)// This example has the same service definition as restful-user-resource
// but uses a different router (CurlyRouter) that does not use regular expressions
//
// POST http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
// GET http://localhost:8080/users/1
//
// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
// DELETE http://localhost:8080/users/1
//type User struct {Id, Name string
}type UserResource struct {// normally one would use DAO (data access object)users map[string]User
}func (u UserResource) Register(container *restful.Container) {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 wellws.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)
}// GET http://localhost:8080/users/1
//
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {id := request.PathParameter("user-id")usr, ok := u.users[id]if !ok {response.AddHeader("Content-Type", "text/plain")response.WriteErrorString(http.StatusNotFound, "User could not be found.")} else {response.WriteEntity(usr)}
}// POST http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) {usr := new(User)err := request.ReadEntity(&usr)if err == nil {u.users[usr.Id] = *usrresponse.WriteEntity(usr)} else {response.AddHeader("Content-Type", "text/plain")response.WriteErrorString(http.StatusInternalServerError, err.Error())}
}// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
func (u *UserResource) createUser(request *restful.Request, response *restful.Response) {usr := User{Id: request.PathParameter("user-id")}err := request.ReadEntity(&usr)if err == nil {u.users[usr.Id] = usrresponse.WriteHeaderAndEntity(http.StatusCreated, usr)} else {response.AddHeader("Content-Type", "text/plain")response.WriteErrorString(http.StatusInternalServerError, err.Error())}
}// DELETE http://localhost:8080/users/1
//
func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) {id := request.PathParameter("user-id")delete(u.users, id)
}func main() {wsContainer := restful.NewContainer()wsContainer.Router(restful.CurlyRouter{})u := UserResource{map[string]User{}}u.Register(wsContainer)log.Printf("start listening on localhost:8080")server := &http.Server{Addr: ":8080", Handler: wsContainer}log.Fatal(server.ListenAndServe())
}

执行:

$ ./restful

客户端调用:

curl -X POST -v -i http://127.0.0.1:8080/users \
-H 'Content-type: application/json' \
-H 'Accept: application/xml' \
-d '{"Id": "1", "Name": "fanguiju"}'curl -X GET -v -i http://127.0.0.1:8080/users/1

代码示例二

package mainimport ("io""log""net/http""github.com/emicklei/go-restful"swagger "github.com/emicklei/go-restful-swagger12"
)type UserResource struct{}func (u UserResource) result(request *restful.Request, response *restful.Response) {io.WriteString(response.ResponseWriter, "this would be a normal response")
}func (UserResource) SwaggerDoc() map[string]string {return map[string]string{"":         "Address doc","country":  "Country doc","postcode": "PostCode doc",}
}func (u UserResource) RegisterTo(container *restful.Container) {ws := new(restful.WebService)ws.Path("/user").Consumes("*/*").Produces("*/*")ws.Route(ws.GET("/{id}").To(u.result).Doc("方法描述:获取用户").Param(ws.PathParameter("id", "参数描述:用户ID").DataType("string")).Param(ws.QueryParameter("name", "用户名称").DataType("string")).Param(ws.HeaderParameter("token", "访问令牌").DataType("string")).Do(returns200, returns500))ws.Route(ws.POST("").To(u.result))ws.Route(ws.PUT("/{id}").To(u.result))ws.Route(ws.DELETE("/{id}").To(u.result))container.Add(ws)
}func returns200(b *restful.RouteBuilder) {b.Returns(http.StatusOK, "OK", "success")
}func returns500(b *restful.RouteBuilder) {b.Returns(http.StatusInternalServerError, "Bummer, something went wrong", nil)
}func main() {wsContainer := restful.NewContainer()// 跨域过滤器cors := restful.CrossOriginResourceSharing{ExposeHeaders:  []string{"X-My-Header"},AllowedHeaders: []string{"Content-Type", "Accept"},AllowedMethods: []string{"GET", "POST"},CookiesAllowed: false,Container:      wsContainer}wsContainer.Filter(cors.Filter)// Add container filter to respond to OPTIONSwsContainer.Filter(wsContainer.OPTIONSFilter)config := swagger.Config{WebServices:    restful.DefaultContainer.RegisteredWebServices(), // you control what services are visibleWebServicesUrl: "http://localhost:8080",ApiPath:        "/apidocs.json",ApiVersion:     "V1.0",// Optionally, specify where the UI is locatedSwaggerPath:     "/apidocs/",SwaggerFilePath: "D:/gowork/src/doublegao/experiment/restful/dist"}swagger.RegisterSwaggerService(config, wsContainer)swagger.InstallSwaggerService(config)u := UserResource{}u.RegisterTo(wsContainer)log.Print("start listening on localhost:8080")server := &http.Server{Addr: ":8080", Handler: wsContainer}defer server.Close()log.Fatal(server.ListenAndServe())}

Go 语言编程 — go-restful RESTful 框架相关推荐

  1. C语言实现OOP——轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍(三)

    文章目录 C 语言编程框架 LW_OOPC 介绍(三) 方案的可扩展性如何? LW_OOPC最佳实践 LW_OOPC的优点: LW_OOPC的缺点: 总结: 幕后花絮: 参考资料: C 语言编程框架 ...

  2. C语言实现OOP——轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍(二)

    轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍 下面,再举一个稍微复杂的例子,它的覆盖面是足够全面的,足以一瞥面向对象编程的3个要素:数据抽象.继承和多态.通过这个例子,我们期望展现出LW_ ...

  3. Go语言编程笔记12:web基础

    Go语言编程笔记12:web基础 图源:wallpapercave.com 开一个新坑,用Go来做web开发.虽然已经从事多年基于LAMP的web开发,但最近学习了Go编程,所以打算借着学习<G ...

  4. perl语言编程 第四版_2020年,5 种 将死的编程语言!

    来源 | 码农网译者 | 小峰 曾几何时,几乎每个人都在使用Perl语言编程.但是那些经常使用的人慢慢地发现,关于这个Perl语言似乎总是有点不对劲.至少我知道有这么个叫做"piecemea ...

  5. Go 语言编程 — Cobra 指令行工具

    目录 文章目录 目录 Cobra(眼镜蛇) Cobra 的核心概念 Cobra 的使用 初始化应用程序的项目框架 main.go 生成应用程序的子命令(SubCmd) 实现 Command 的功能 为 ...

  6. Go 语言编程 — 项目布局规范

    目录 文章目录 目录 项目布局(Project Layout)规范 程序目录 /cmd(Command)包 /internal /pkg(Package) /vendor Service(服务端)应用 ...

  7. 换硬币c语言编程_如何才能成为编程高手?别人都不告诉你的东西,我来说给你听...

    在IT行业摸爬滚打十几年的应该知道,下面简单说说关于编程需要掌握的技术与相关知识.很多人感觉编程很难.很多人问我,我英语不好,数学不好能做编程吗? 我非常肯定的说,能做编程.编程的领域很广泛.细分出来 ...

  8. 《Go 语言编程之旅》送煎架和站长写的书

    Go语言是一种开源编程语言,可轻松构建简单.可靠且高效的软件. Go语言在2009年首次亮相,是谷歌开发的一种通用型语言.与Python等其他编程语言相比,Go语言具有多个优势,这也是它值得关注的地方 ...

  9. perl语言编程 第四版_2020年,5 种 将死的编程语言

    来源 | 码农网 译者 | 小峰 曾几何时,几乎每个人都在使用Perl语言编程.但是那些经常使用的人慢慢地发现,关于这个Perl语言似乎总是有点不对劲.至少我知道有这么个叫做"pieceme ...

最新文章

  1. 【FFmpeg】警告:[mpegts] H.264 bitstream error, startcode missing, size 0
  2. 关于大XML文件与大节点处理(System.Xml.XmlTextReader)
  3. yum mysql mariadb 目录_CentOS用yum安装、配置MariaDB
  4. mysql 升序 字段值为NULL 排在后面
  5. php 文件管理系统_如何编写程序实现图书管理系统的个人图书借阅查询功能
  6. java 文件比较_java 四种写文件比较
  7. python中title用法_在CSV fi中使用Title()
  8. 美团--美团骑手包裹区间分组
  9. Win10窗口侧边栏设置Win7模式
  10. 一个程序让你学会C++构造函数与重载构造、析构函数【C++类的经典使用案例】
  11. numpy.mean() in Python
  12. wepack中loader的分类
  13. WCF中如何修改MaxItemsInObjectGraph的限制
  14. Android UI设计之十一自定义ViewGroup,打造通用的关闭键盘小控件ImeObser
  15. 快速撑握C#知识点之类的数据成员
  16. 【linux基础】cuDNN版本查询
  17. 20165309 《网络对抗技术》实验二:后门原理与实践
  18. php高级视频教程下载_50个G的PHP视频教程全集下载
  19. 京东淘宝天猫户外服饰行业数据分析(电商数据查询软件)
  20. 讯时O口MX8网关对接昆石软交换vos3000

热门文章

  1. 使用TensorFlow1.0及以上版本的问题
  2. 研究人员利用非线性原理为机器人创造出类似昆虫的步态,脑机接口也可以使用...
  3. 科学家提出了忆阻性神经混合芯片 这一概念
  4. 机器学习(5)--化无限为有限
  5. 初识、理解适配器模式
  6. 中科大博士带头搬砖!这家上市公司其实是最隐秘的AI高手
  7. 召集最强的智,昇腾计算产业射出一支「穿云箭」
  8. Python之父,现在成为微软打工人
  9. PyTorch 1.7发布:支持CUDA 11、Windows分布式训练
  10. LeCun之后Jeff Dean离奇躺枪,哥大黑人女博士:你们得引用我!