我试图在golang上写一个restful api。对于http路由器我使用gin-gonic,与我使用gorm的数据库进行交互。 包主要
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
var db *gorm.DB
type Person struct {
ID uint `json:"id"`
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
}
func main() {
// NOTE: See we’re using = to assign the global var
// instead of := which would assign it only in this function
db, err := gorm.Open("postgres", fmt.Sprintf("host=localhost sslmode=disable user=postgres password="))
if err != nil {
fmt.Println(err)
}
defer db.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET("/people/", GetPeople)
r.GET("/people/:id", GetPerson)
r.POST("/people", CreatePerson)
r.Run(":8080")
}
func CreatePerson(c *gin.Context) {
var person Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}
func GetPerson(c *gin.Context) {
id := c.Params.ByName("id")
var person Person
if err := db.Where("id = ?", id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, person)
}
}
func GetPeople(c *gin.Context) {
var people []Person
if err := db.Find(&people).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, people)
}
}
如何将代码拆分为多个文件,以便单独的资源位于单独的文件中?如何在另一个文件中使用路由器和数据库?
更新
使用这样的结构:
.
└── app
├── users.go
├── products.go
└── main.go
我有两个问题:
products.go
和users.go
get
,create
...),这可以通过函数声明中的前缀来解决,例如CreateUser
,CreateProduct
等。但它可能会通过将代码放入另一个包来解决这个问题main.go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
var (
db *gorm.DB
r *gin.Engine
)
func init() {
db, err := gorm.Open("postgres", fmt.Sprintf("host=localhost sslmode=disable user=postgres password="))
if err != nil {
fmt.Println(err)
}
defer db.Close()
r = gin.Default()
}
func main() {
r.Run(":8080")
}
products.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func init() {
db.AutoMigrate(&Product{}) // db -> nil
r.GET("/products", get)
}
func get(c *gin.Context) {
var product Product
db.First(&product, 1)
c.JSON(200, gin.H{
"product": product,
})
}
users.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
type User struct {
gorm.Model
Name string
}
func init() {
db.AutoMigrate(&User{}) // db -> nil
r.GET("/users", get)
}
// ./users.go:19: get redeclared in this block
// previous declaration at ./products.go:20
func get(c *gin.Context) {
var user User
db.First(&user, 1)
c.JSON(200, gin.H{
"user": user,
})
}
答案 0 :(得分:1)
由于您的db
var是在包级别定义的,因此它基本上是该包的全局变量,可以在该包中的任何文件中引用。
例如,在这样的项目中:
.
└── app
├── a.go
├── b.go
├── c.go
└── main.go
如果在软件包级别的main.go中定义了db
,如示例所示,则文件a.go,b.go和c.go中的代码可以使用db
。< / p>
它也是另一种方式,a.go,b.go和c.go中定义的任何资源处理程序都可以在main.go中引用。这意味着在每个文件中你可以定义一个带路由器,gin路由器的功能,并设置相应的处理程序,然后在main.go的主要功能中调用那些在路由器中传递的函数{{1 }}
首先,您在init函数内部调用r
,这意味着在init返回后,您的defer db.Close()
将被关闭,这绝对不是您想要的。在main中使用db
很好,因为当你的应用程序终止时,main会终止,那时关闭数据库是有意义的,但是当init终止你的应用程序甚至没有正确启动时,主要是刚刚执行而你仍需要你的defer db.Close()
。
如果你想在每个文件中使用db
函数来执行特定于该文件的初始化,你必须确保这些初始化函数所依赖的,在执行之前初始化
在您的示例中,所有初始化函数都依赖于init
和db
,因此您需要确保这两个函数不是r
。我不完全确定在Go中,执行的顺序是单个包中的多个init函数,但我确切知道在初始化函数执行之前初始化了包级别变量表达式。
所以你可以做的是使用函数调用初始化两个包级变量,如下所示:
nil
至于你的第二个问题,在Go package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
var (
db = func() *gorm.DB {
db, err := gorm.Open("postgres", fmt.Sprintf("host=localhost sslmode=disable user=postgres password="))
if err != nil {
// if you can't open a db connection you should stop the app,
// no point in continuing if you can't do anything useful.
panic(err)
}
return db
}() // <- call the anon function to get the db.
r = gin.Default()
)
func main() {
// you can call defer db.Close() here but you don't really need to
// because after main exists, that is, your app terminates, db
// will be closed automatically.
r.Run(":8080")
}
中是一个特殊情况,我的意思是你可以在一个包中有多个init函数,甚至在一个文件中。您声明的任何其他标识符都不是这样。
这意味着在包内,并在包级别声明,您只能有一个init
标识符,一个db
标识符,只有一个get
标识符,等等。使用suffiex例如User
或包getUser
完全取决于您。
请注意,您可以重新声明另一个范围内的标识符 ,假设您在包级别有user.Get
,然后在同一个pacakge中声明的函数可以在里面它自己的范围声明了一个像type User struct { ...
这样的变量,尽管它可能不是最好的想法。
有关详细信息,请参阅: Package initialization
如果要将代码拆分为多个包,只需将文件放入单独的文件夹中,并确保文件顶部的var User = "whatever"
声明具有正确的包名称。
以下是一个例子:
package
现在您的└── app/
├── main.go
├── product/
│ ├── product.go
│ └── product_test.go
└── user/
├── user.go
└── user_test.go
代码看起来像这样。
app/user/user.go
你的package user
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
var db *gorm.DB
type User struct {
gorm.Model
Name string
}
// custom and exported Init function, this will not be called automatically
// by the go runtime like the special `init` function and therefore must be called
// manually by the package that imports this one.
func Init(gormdb *gorm.DB, r *gin.Engine) {
db = gormdb // set package global
db.AutoMigrate(&User{})
r.GET("/users", get)
}
func get(c *gin.Context) {
var user User
db.First(&user, 1)
c.JSON(200, gin.H{
"user": user,
})
}
...
app/product/product.go
您的入口点为package product
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
var db *gorm.DB
type Product struct {
gorm.Model
Code string
Price uint
}
// custom and exported Init function, this will not be called automatically
// by the go runtime like the special `init` function and therefore must be called
// manually by the package that imports this one.
func Init(gormdb *gorm.DB, r *gin.Engine) {
db = gormdb // set package global
db.AutoMigrate(&Product{})
r.GET("/products", get)
}
func get(c *gin.Context) {
var product Product
db.First(&product, 1)
c.JSON(200, gin.H{
"product": product,
})
}
...
app/main.go