我正在尝试在 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.goget,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,
})
}
最佳答案
因为您的 db var 是在包级别定义的,所以它基本上是该包的全局变量,可以在该包中的任何文件中引用。
例如,在这样的项目中:
.
└── app
├── a.go
├── b.go
├── c.go
└── main.go
如果 db 是在包级别的 main.go 中定义的,如您的示例,则文件 a.go、b.go 和 c.go 中的代码可以使用 db 。
反之亦然,在 a.go、b.go 和 c.go 中定义的任何资源处理程序都可以在 main.go 中引用。这意味着在这些文件中的每一个中,您都可以定义一个函数,它接受一个路由器,gin 路由器,并设置相应的处理程序,然后在 main.go 的主要函数中,您调用传递给路由器的那些函数 r.
首先,您在 init 函数内部调用 defer db.Close(),这意味着在 init 返回后您的 db 立即关闭,这绝对是不是你想要的。在 main 中使用 defer db.Close() 很好,因为 main 在您的应用程序终止时终止,此时关闭数据库是有道理的,但是当 init 终止时您的应用程序甚至没有正常启动,main刚刚被执行,你仍然需要你的 db。
如果你想在每个文件中使用 init 函数来执行特定于该文件的初始化,你必须确保无论这些 init 函数依赖什么,都在它们被执行之前被初始化 .
在您的示例中,所有初始化函数都依赖于 db 和 r,因此您需要确保这两个不是 nil。我不确定在 Go 中,单个包中的多个 init 函数的执行顺序是什么,但我可以肯定的是包级变量表达式在执行 init 函数之前得到初始化。
所以你可以做的是使用函数调用来初始化两个包级变量,如下所示:
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")
}
关于你的第二个问题,在 Go 中,init 是一个特例,我的意思是你可以在一个包内有多个 init 函数,甚至在单个文件。这不适用于您声明的任何其他标识符。
这意味着在包内,并在包级别声明,你只能有一个 db 标识符,一个 get 标识符,只有一个 User 标识符等。是否使用后缀,例如getUser 或 packages user.Get 完全由您决定。
请注意,您可以在另一个作用域中重新声明标识符,假设您在包级别有type User struct { ...,然后在中声明了一个函数同一个 pacakge 可以在其自己的范围内声明一个变量,如 var User = "whatever",尽管这可能不是编译时的最佳想法。
详情请见: Package initialization
如果您想将代码拆分到多个包中,只需将文件放入单独的文件夹中,并确保文件顶部的 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 ...
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")
}
关于go - 如何将我的资源拆分成多个文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47115731/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl