douxue4242 2017-11-04 21:18
浏览 39
已采纳

如何将我的资源分成多个文件

I'm trying to write a restful api on golang. For http router I use gin-gonic, to interact with the database I use gorm. package main

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)
    }
}

How do I split the code into multiple files so that a separate resource is in a separate file? How to use the router and database in another file?

UPDATE

With structure like this:

.
└── app
    ├── users.go
    ├── products.go
    └── main.go

I have 2 problems:

  1. db == nil in products.go and users.go
  2. Redeclaration function (get, create ...) in different files, this solve by prefix in function declaration like CreateUser, CreateProduct, etc. But it may be solved by put code into another packages

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,
    })
}
  • 写回答

1条回答 默认 最新

  • dqybeh2884 2017-11-04 21:36
    关注

    Since your db var is defined at the package level it's basically a global for that package an can be referenced in any file that lives in that package.

    For example, in a project like this:

    .
    └── app
        ├── a.go
        ├── b.go
        ├── c.go
        └── main.go
    

    If db is defined inside main.go at the package level, as in your example, then code in files a.go, b.go, and c.go can use db.

    It works the other way as well, any resource handlers defined in a.go, b.go, and c.go can be reference in main.go. Which means that in each of those files you can define a function that takes a router, the gin router, and sets the corresponding handlers, then inside main.go's main function you call those functions passing in the router r.


    Update

    First off, you're calling defer db.Close() inside of you init function, which means that right after init returns your db gets closed which is absolutely not what you want. Using defer db.Close() in main is fine because main terminates when your app terminates, closing the db at that point makes sense, but when init terminates your app didn't even start properly, the main is just getting executed and you still need your db.

    If you want to use the init functions in each file to do initialization specific to that file, you have to ensure that whatever those init functions depend on, is initialized before they get executed.

    In your example all of your init functions depend on db and r so you need to make sure these two are not nil. I'm not exactly sure what, in Go, the order of execution is for multiple init functions in a single package but what I know for sure is that package level variable expressions get initialized before the init functions are executed.

    So what you can do is to use a function call to initialize the two package level variables like so:

    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")
    }
    

    As to your second problem, in Go init is a special case and by that I mean that you can have multiple init functions inside a single package, and even inside a single file. This is not true of any other identifiers that you declare.

    That means that inside a package, and declared at the package level, you can have only one db identifier, one get identifier, only one User identifier, etc. Whether you use suffiex e.g. getUser or packages user.Get is entirely up to you.

    Note that you can redeclare an identifier in another scope, let's say you have type User struct { ... at the package level, and then a function declared in the same pacakge can inside its own scope declare a variable like so var User = "whatever", although it's probably not the best idea it will compile.

    For more details see: Package initialization


    Update 2

    If you want to split your code into multiple packages you just put your files into separate folders and make sure that the package declaration at the top of your file has the correct package name.

    Here's an example:

    └── app/
        ├── main.go
        ├── product/
        │   ├── product.go
        │   └── product_test.go
        └── user/
            ├── user.go
            └── user_test.go
    

    Now your app/user/user.go code could look something like this.

    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,
        })
    }
    

    your 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,
        })
    }
    

    And your entry point at app/main.go ...

    package main
    
    import (
        "fmt"
    
        // This assumes that the app/ folder lives directly in $GOPATH if that's
        // not the case the import won't work.
        "app/product"
        "app/user"
    
        "github.com/gin-gonic/gin"
        "github.com/jinzhu/gorm"
        _ "github.com/jinzhu/gorm/dialects/postgres"
    )
    
    func main() {
        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()
    
        // manually initialize imported packages
        user.Init(db, r)
        product.Init(db, r)
    
        r.Run(":8080")
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 ensp路由器启动不了一直报#
  • ¥50 安卓10如何在没有root权限的情况下设置开机自动启动指定app?
  • ¥15 ats2837 spi2从机的代码
  • ¥200 wsl2 vllm qwen1.5部署问题
  • ¥100 有偿求数字经济对经贸的影响机制的一个数学模型,弄不出来已经快要碎掉了
  • ¥15 数学建模数学建模需要
  • ¥15 已知许多点位,想通过高斯分布来随机选择固定数量的点位怎么改
  • ¥20 nao机器人语音识别问题
  • ¥15 怎么生成确定数目的泊松点过程
  • ¥15 layui数据表格多次重载的数据覆盖问题