Golang 使用 Beego 与 Mgo 开发的示例程序

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介:

   当我发现 beego 框架时感觉非常激动。我只用了大约 4 个小时就将一个现有的 Web 应用程序移植到了该框架上并做了一些端对端测试的调用扩展。我想要与你分享这个基于 beego 的站点。

我构建了一个具有以下功能的示例 Web 应用程序:

  1. 实现了 2 个通过 mgo 驱动拉取 MongoDB 数据的 Web 调用。

  2. 使用 envconfig 配置环境变量作为参数。

  3. 通过 goconvey 书写测试用例。

  4. 结合我的 logging 包。

这个示例的代码可以在 GoingGo 帐户下的 GitHub 仓库中找到:https://github.com/goinggo/beego-mgo。

你可以拉取并运行它。它使用了我在 MongoLab 创建的公开 MongoDB 数据库。你会需要安装 git 和 bazaar 以保证能够使用 go get 来将它安装到你系统中。

go get github.com/goinggo/beego-mgo

使用 zscripts 文件夹里的脚步可以快速运行或测试 Web 应用程序。

Web 应用程序代码结构

让我们来看一下项目结构和不同文件夹的功能:

  • app:包含 business、model 和 service 层。

  • app/models:用于 business 和 service 层的数据结构。

  • app/services:用于为不同服务提供基本函数。可以是针对数据库或 Web 调用的函数。

  • app/business:被 controller 和处理 business 规则的函数所调用。多种服务的组合调用可用于实现更大层面上的功能。

  • controllers:URL 或 Web API 调用的切入点。控制器会直接调用 business 层的函数来直接处理请求。

  • routes:用于 URL 和控制器代码的映射。

  • static:用于存放脚本、CSS 和图片等静态资源。

  • test:可使用 go test 运行的测试用例。

  • utilities:用于支持 Web 应用程序的代码。数据库操作和 panic 处理的样例与抽象化代码。

  • views:用于存放模板文件。

  • zscripts:帮助更方便构建、运行和测试 Web 应用程序的脚本。

控制器、模型和服务

这些层的代码组成了 Web 应用程序。我的框架背后的理念是尽可能的抽象化代码为样板。这就要求我实现一个 base 控制器包和 base 服务包。

base 控制器包

base 控制器包由所有控制器所要求的默认抽象的控制器行为来组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
type (
     BaseController struct {
         beego.Controller
         services.Service
     }
)
                                      
func ( this  *BaseController) Prepare() {
     this .UserId =  "unknown"
     tracelog.TRACE( this .UserId,  "Before" "UserId[%s] Path[%s]" this .UserId,  this .Ctx.Request.URL.Path)
                                    
     var err error
     this .MongoSession, err = mongo.CopyMonotonicSession( this .UserId)
     if  err != nil {
         tracelog.ERRORf(err,  this .UserId,  "Before" this .Ctx.Request.URL.Path)
         this .ServeError(err)
     }
}
                                      
func ( this  *BaseController) Finish() {
     defer func() {
         if  this .MongoSession != nil {
             mongo.CloseSession( this .UserId,  this .MongoSession)
             this .MongoSession = nil
         }
     }()
                                          
     tracelog.COMPLETEDf( this .UserId,  "Finish" this .Ctx.Request.URL.Path)
}

一个名为 BaseController 的新类型直接由类型 beego.Controller 和 services.Service 嵌入而组成。这样做就可以直接获得这两个类型的所有字段和方法给 BaseController,并直接操作这些字段和方法。

服务包

服务包用于样板化所有服务所要求的代码:

1
2
3
4
5
6
7
8
9
10
type (
     Service struct {
         MongoSession *mgo.Session
         UserId       string
     }
)
                                
func ( this  *Service) DBAction(databaseName string, collectionName string, mongoCall mongo.MongoCall) (err error) {
     return  mongo.Execute( this .UserId,  this .MongoSession, databaseName, collectionName, mongoCall)
}

在 Service 类型中,包含了 Mongo 会话和用户 ID。函数 DBAction 提供了运行 MongoDB 命令和查询的抽象层。

实现一个 Web 调用

基于 base 类型和样板函数,我们现在可以实现一个 Web 调用了:

Buoy Controller

类型 BuoyController 实现了两个结合 BaseController 和 business 层 Web API 调用。我们主要关注名为 Station 的 Web 调用。

1
2
3
4
5
6
7
type BuoyController struct {
     bc.BaseController
}
                          
func ( this  *BuoyController) Station() {
     buoyBusiness.Station(& this .BaseController,  this .GetString( ":stationId" ))
}

类型 BuoyController 直接由单独的 BaseController 组成。通过这种组合方式,BuoyController 自动获得了已经自定义实现的 Prepare和 Finish 方法以及所有 beego.Controller 的字段。

函数 Station 通过下面路由分发。stationId 作为 URL 的最后一项。当这个路由被请求时,一个 BuoyController 对象会被实例化并执行函数 Station

beego.Router("/station/:stationId", &controllers.BuoyController{}, "get:Station")

函数 Station 会在底层调用 business 层的代码来处理请求。

Buoy Business

Buoy Business 包实现了 BuoyController 的 business 层。让我看看在 BuoyController 中的函数 Station 所调用的 business 层的代码是怎样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func Station(controller *bc.BaseController, stationId string) {
     defer bc.CatchPanic(controller,  "Station" )
                       
     tracelog.STARTEDf(controller.UserId,  "Station" "StationId[%s]" , stationId)
                       
     buoyStation, err := buoyService.FindStation(&controller.Service, stationId)
     if  err != nil {
         ServeError(err)
         return
     }
                          
     controller.Data[ "json" ] = &buoyStation
     controller.ServeJson()
     tracelog.COMPLETED(controller.UserId,  "Station" )
}

你可以看到 business 层的函数 Station 处理了整个请求的逻辑。这个函数还使用了Buoy Service 来处理与 MongoDB 的交互。

Buoy Service

Buoy Service 包实现了与 MongoDB 的交互。让我们来看下被 business 层的函数 Station 所调用的函数 FindStation 的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func FindStation(service *services.Service, stationId string) (buoyStation *buoyModels.BuoyStation, err error) {
     defer helper.CatchPanic(&err, service.UserId,  "FindStation" )
                  
     tracelog.STARTED(service.UserId,  "FindStation" )
                  
     queryMap := bson.M{ "station_id" : stationId}
                  
     tracelog.TRACE(service.UserId,  "FindStation" "Query : %s" , mongo.ToString(queryMap))
                  
     buoyStation = &buoyModels.BuoyStation{}
                  
     err = service.DBAction(Config.Database,  "buoy_stations" , func(collection *mgo.Collection) error {
         return  collection.Find(queryMap).One(buoyStation)
     })
                  
     if  err != nil {
         tracelog.COMPLETED_ERROR(err, service.UserId,  "FindStation" )
         return  buoyStation, err
     }
                  
     tracelog.COMPLETED(service.UserId,  "FindStation" )
                  
     return  buoyStation, err
}

函数 FindStation 用于准备通过 DBAction 函数进行与 MongoDB 的查询与执行操作。

端到端测试

我们现在有一个 URL 可以路由到一个控制器以及相应的 business 与 service 层的逻辑,它可以通过以下方式测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func TestStation(t *testing.T) {
     r, _ := http.NewRequest( "GET" "/station/42002" , nil)
     w := httptest.NewRecorder()
                  
     beego.BeeApp.Handlers.ServeHTTP(w, r)
     err := struct {
         Error string
     }{}
              
     json.Unmarshal(w.Body.Bytes(), &err)
              
     Convey( "Subject: Test Station Endpoint\n" , t, func() {
          Convey( "Status Code Should Be 200" , func() {
             So(w.Code, ShouldEqual,  200 )
          })
               
          Convey( "The Result Should Not Be Empty" , func() {
             So(w.Body.Len(), ShouldBeGreaterThan,  0 )
          })
                
         Convey( "The Should Be No Error In The Result" , func() {
             So(len(err.Error), ShouldEqual,  0 )
          })
     })
}

测试创建了一个某个路由的虚拟调用。这种做法很赞,因为我们不需要真正地去启动一个 Web 应用来测试代码。使用 goconvey 我们能够创建非常优雅的输出且易于阅读。

以下是一个测试失败的样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Subject: Test Station Endpoint
   Status Code Should Be 200
   The Result Should Not Be Empty
   The Should Be No Error In The Result
Failures:
/Users/bill/Spaces/Go/Projects/src/github .com /goinggo/beego-mgo/test/endpoints/buoyEndpoints_test .go
Line 35:
Expected:  '200'
Actual:  '400'
(Should be equal)
          
/Users/bill/Spaces/Go/Projects/src/github .com /goinggo/beego-mgo/test/endpoints/buoyEndpoints_test .go
Line 37:
Expected:  '0'
Actual:  '9'
(Should be equal)
          
          
3 assertions thus far
          
--- FAIL: TestStation-8 (0.03 seconds)

以下是一个测试成功的样例:

1
2
3
4
5
6
7
8
9
Subject: Test Station Endpoint
      
   Status Code Should Be 200
   The Result Should Not Be Empty
   The Should Be No Error In The Result
      
3 assertions thus far
      
--- PASS: TestStation-8 (0.05 seconds)

结论

花点时间下载这个项目随便看看。我尽最大努力让你能够清晰地看到我所想要展示给你的重点。beego 框架让你能够非常轻松地实现抽象和样板代码,遵照 go 的风格集成 go 测试、运行及部署。











本文转自 ponpon_ 51CTO博客,原文链接:http://blog.51cto.com/liuxp0827/1358391,如需转载请自行联系原作者
相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
1月前
|
中间件 Go API
Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架
本文概述了Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架。
87 1
|
3月前
|
Go API
Golang语言开发注意事项
这篇文章总结了Go语言开发中的注意事项,包括语法细节、注释使用、代码风格、API文档的利用以及如何使用godoc工具来生成文档。
46 2
|
4月前
|
监控 测试技术 API
|
4月前
|
监控 Java 测试技术
|
4月前
|
存储 安全 Go
|
5月前
|
编译器 Go C语言
通过例子学习在golang中调试程序
【7月更文挑战第4天】Go语言支持使用cgo进行汇编调试,官方文档在golang.org/doc/asm。注意,调试Go运行时可能遇到变量不可用或行号错误,需谨慎使用step命令。
78 1
通过例子学习在golang中调试程序
|
4月前
|
监控 Serverless Go
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
|
4月前
|
Java Serverless Go
Golang 开发函数计算问题之在 Golang 中避免 "concurrent map writes" 异常如何解决
Golang 开发函数计算问题之在 Golang 中避免 "concurrent map writes" 异常如何解决
|
4月前
|
Serverless Go
Golang 开发函数计算问题之defer 中的 recover() 没有捕获到 如何解决
Golang 开发函数计算问题之defer 中的 recover() 没有捕获到 如何解决
|
5月前
|
监控 Go
golang开发 gorilla websocket的使用
【7月更文挑战第11天】在Golang中, 使用Gorilla WebSocket库可轻松实现WebSocket通信。安装库: `go get github.com/gorilla/websocket`。创建连接: `websocket.DefaultDialer.Dial("ws://url", nil)`。发送消息: `conn.WriteMessage(websocket.TextMessage, []byte("Hello"))`。接收消息: 循环调用`conn.ReadMessage()`。适用于实时聊天或股票行情等场景。
139 0