Go打造REST Server【三】:用Web框架来实现
在这一部分中,我们将使用Go中最流行的Web框架之一Gin重新实现我们的REST服务器。
介绍
在这一部分中,我们将使用Go中最流行的Web框架之一Gin重新实现我们的REST服务器。
选择Web框架
Go现在有几个流行的Web框架,它们都有其优点。我们的目标不是对这些框架进行冗长的比较和讨论,而是研究使用框架的代码与不使用框架的代码相比如何。
选择Gin是因为它是最受欢迎的项目之一(从GitHub的star数来看),而且它看起来很小且易于上手和使用。Gin的文档还有很多不足之处,但该框架非常直观,很容易上手。
Gin的优点在于它不会强迫使用任何特定风格的应用程序开发(例如MVC)。使用Gin几乎感觉就像在没有框架的情况下编写代码,只是可以获得很多工具和好东西来用更少的代码实现目标。
Gin的路由
我们再main函数使用新的Gin并注册路由:
func main() {
router := gin.Default()
server := NewPageServer()
router.POST("/page/", server.createPageHandler)
router.GET("/page/", server.getAllPagesHandler)
router.DELETE("/page/", server.deleteAllPagesHandler)
router.PUT("/page/", server.updatePageHandler)
router.GET("/page/{id:[0-9]+}/", server.getPageHandler)
router.DELETE("/page/{id:[0-9]+}/", server.deletePageHandler)
router.GET("/tag/", server.tagHandler)
router.GET("/due/", server.dueHandler)
log.Fatal(http.ListenAndServe("localhost:8880", router))
}
对gin.Default()的调用返回一个默认引擎,这是Gin的主要类型,充当路由并提供其他功能。具体来说,Default仅注册用于崩溃恢复和日志记录的基本中间件,稍后将详细介绍中间件。
路由注册现在应该看起来很熟悉,它与gorilla/mux版本略有相似,但略有不同:
- 不是选择HTTP方法作为路由并附加Go方法调用,而是将其编码在注册名称中;例如,router.POST而不是router.HandleFunc(…).Methods(“POST”)。
- 虽然Gorilla在路由中支持正则表达式匹配,但Gin不支持。
Handlers
让我们来看看一些带有Gin的处理程序,从最简单的开始,这里是getAllPagesHandler:
func (p *PageServer) getAllPagesHandler(c *gin.Context) {
allTasks := p.store.GetAllPages()
c.JSON(http.StatusOK, allTasks)
}
这里有一些有趣的事情需要注意:
- Gin的处理程序没有标准的Go HTTP处理程序签名;相反,他们只需要一个 gin.Context,它可以用来分析请求并构造响应。Gin确实可以通过gin.WrapF和 gin.WrapH辅助函数与标准处理程序进行交互。
- 与我们服务器的早期版本相比,不需要手动记录每个请求,因为Gin的默认记录中间件已经这样做了(例如终端颜色和报告每个请求的处理时间)。
- 我们也不必再实现renderJSON函数,因为Gin有自己的Context.JSON来将JSON渲染为响应。
现在让我们检查一个稍微复杂的有参数的处理程序:
func (p *PageServer) getPageHandler(c *gin.Context) {
id, err := strconv.Atoi(c.Params.ByName("id"))
if err != nil {
c.String(http.StatusBadRequest, err.Error())
return
}
task, err := p.store.GetPage(id)
if err != nil {
c.String(http.StatusNotFound, err.Error())
return
}
c.JSON(http.StatusOK, task)
}
这里要注意的部分是参数处理,Gin通过Context.Params提供对路由参数(以冒号开头的路由部分,如:id)的访问。
然而,与Gorilla不同的是,Gin在其路由中不支持正则表达式(这可能是出于性能考虑,因为Gin以快速路由而为称)。因此,我们必须处理id参数的整数解析。
绑定
我们要详细研究的最后一个处理程序是createTaskHandler;它处理一个携带重要数据的请求:
type PageRequest struct {
Text string `json:"text"`
Tags []string `json:"tags"`
Due time.Time `json:"due"`
}
func (p *PageServer) createPageHandler(c *gin.Context) {
var ret PageRequest
if err := c.ShouldBindJSON(&ret); err != nil {
c.String(http.StatusBadRequest, err.Error())
}
id := p.store.CreatePage(ret.Text, ret.Tags, ret.Due)
c.JSON(http.StatusOK, gin.H{"Id": id})
}
Gin具有用于将请求绑定到Go数据的重要基础功能。在这种情况下绑定意味着解析请求的内容(可以是JSON、YAML或其他格式),验证它们并将它们的值分配给Go结构体。在这里,我们在没有任何验证的情况下为PageRequest使用了一种非常基本的绑定形式,但值得一试Gin提供的更高级的选项。
createPageHandler的Gin版本比我们的早期版本短很多,因为ShouldBindJSON正在执行从请求中解析JSON的工作。
需要注意的另一件事是,我们现在不需要响应ID的一次性结构。相反,我们使用gin.H,它只是map[string]interface{}的别名,非常有效地以最少的类型和语法构建响应。
Gin附加功能
在这个例子中,我们只使用了Gin为Web应用程序开发人员提供的一小部分功能。Gin带有许多预先打包的附加功能,例如常用的中间件、身份验证和用于呈现HTML模板的功能。如果没有框架,这些都很难实现,但使用Gin肯定会使其更快,代码更少,至少对于简单的情况是这样。
限制
Web框架便利性的另一面是使用它们时可能会遇到的限制和风格不匹配,在我们的简单示例中,我们已经遇到了一个限制:Gin路由中缺乏正则表达式支持,这意味着复杂的路由匹配需要更多代码来解析和验证。
任何包和工具都可能有局限性,但框架因其非常普遍而使局限性变得更加重要。我们会在 Gorilla/mux中发现一个限制,这将成为我们应用程序的障碍。然后我们可以用另一个路由包替换它。虽然过渡无疑会产生一些成本,但其影响将是局部的,因为只有路由配置受到影响。
更多推荐
所有评论(0)