英文:
golang gin-gonic and splitting files in package
问题
我对Go语言还不太熟悉,但我会尽力帮助你理解代码中的问题。根据你提供的代码,我看到了一些可能导致问题的地方。
首先,在apirest.go
文件中的init
函数中,你定义了一个全局变量Router
,但是在函数内部又重新声明了一个同名的局部变量Router
,这会导致全局变量没有被赋值。你应该将Router := gin.New()
修改为Router = gin.New()
,以便正确赋值给全局变量。
其次,在todoCRUD.go
文件中的LoadTodo
函数中,你尝试使用Router.Group
,但是报错提示undefined: Router
,这是因为你在apirest.go
文件中重新声明了局部变量Router
,导致全局变量Router
没有被引用到。你可以将var Router *gin.Engine
修改为Router *gin.Engine
,以便在todoCRUD.go
文件中正确引用全局变量。
另外,你在maincode.go
文件中使用了apirest.Router
作为manners.ListenAndServe
的参数,但是在apirest.go
文件中并没有导出Router
变量(即首字母大写),导致无法在其他包中引用。你可以将var Router *gin.Engine
修改为var Router *gin.Engine
,以便在其他包中正确引用。
希望这些修改能够解决你的问题。如果还有其他问题,请随时提问。
英文:
I am quite new to go and try to learn, I am setting up an application with a gin-gonic server.
I managed to have it to work with everything in on main package, I would like to organize it better with all the apirest related in a package (which I manage) AND to split each group CRUD by file.
so in the "main" file of the package within the init function, I defined :
Router := gin.New()
Router.Use(gin.Logger())
Router.Use(gin.Recovery())
and I though that I would be able in each other files of the package to use it like that :
v1 := Router.Group("/api/v1/todos")
v1.Use(AuthRequired())
{
v1.POST("/", CreateTodo)
v1.GET("/", FetchAllTodo)
v1.GET("/:id", FetchSingleTodo)
v1.PUT("/:id", UpdateTodo)
v1.DELETE("/:id", DeleteTodo)
}
or
packagename.Router.Group
but none are working and I get :
> undefined: Router in Router.Group
Stéphane
PS : I did setup a sub folder for the package and I am able to go build it if it's in one file.
I though that the solution was to declare the Router variable as *gin.Engine but while it's compiling fine, I get a panic error on the Router.Group call
let me post some more part of code :
maincode.go :
package main
import (
"fmt"
"./apirest"
"github.com/braintree/manners"
)
func main(){
fmt.Printf("hello world.\n")
//router.Run()
manners.ListenAndServe(":8080", apirest.Router)
}
Then I split the apirest package in 2 files :
apirest.go (I did manually cleanup, maybee I missed an import) :
package apirest
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/braintree/manners"
"os"
"os/signal"
)
//Router pour gérer l'api
var Router *gin.Engine
/*
MAIN FUNCTION
*/
func init() {
Router := gin.New()
Router.Use(gin.Logger())
Router.Use(gin.Recovery())
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func(){
for sig := range c {
// sig is a ^C, handle it
fmt.Printf("ctl+c catched "+sig.String())
manners.Close()
}
}()
LoadMonitor()
}
and then the todoCRUD.go file that handle all the declaration, the todoCRUD.go and apirest.go are in the same subfolder apirest (and compile ok) :
package apirest
import (
"fmt"
"github.com/gin-gonic/gin"
"gopkg.in/validator.v2"
"github.com/jinzhu/gorm"
"strconv"
"net/http"
"time"
)
//Todo definition d'un element todo
type Todo struct {
gorm.Model
CreatedAt time.Time
UpdatedAt time.Time
OwnerID int `json:"ownerid" validate:"nonzero"`
URL string `json:"url"`
}
//TransformedTodo version pour le retour d'api sans certaines infos
type TransformedTodo struct {
ID uint `json:"id"`
CreatedAt time.Time
UpdatedAt time.Time
OwnerID uint `json:"ownerid"`
URL string `json:"url"`
}
//LoadTodo permet de lancer le mappage todos
func LoadTodo(){
v1 := Router.Group("/api/v1/todos")
{
v1.POST("/", CreateTodo)
v1.GET("/", FetchAllTodo)
v1.GET("/:id", FetchSingleTodo)
v1.PUT("/:id", UpdateTodo)
v1.DELETE("/:id", DeleteTodo)
}
}
//CreateTodo génération d'un todo
func CreateTodo(c *gin.Context) {
owner, _ := strconv.Atoi(c.PostForm("ownerid"))
todo := Todo{
OwnerID: owner,
URL: c.PostForm("url"),
};
v := validator.NewValidator()
if errs := v.Validate(todo); errs!=nil {
errors := errs.(validator.ErrorMap)
var errOuts []string
for f, e := range errors {
errOuts = append(errOuts, fmt.Sprintf("\t - %s (%v)\n", f, e))
}
//c.JSON(500, gin.H{"Error": errs.Error()})
c.JSON(500, gin.H{"Erreur sur le(s) champ(s) : ": errOuts})
} else {
db, _ := Database()
defer db.Close()
db.Save(&todo)
c.JSON(http.StatusCreated, gin.H{"status" : http.StatusCreated, "message" : "Todo item created successfully!", "resourceId": todo.ID})
}
}
//FetchAllTodo récupération de tous les todos
func FetchAllTodo(c *gin.Context) {
var todos []Todo
var _todos []TransformedTodo
db, _ := Database()
defer db.Close()
db.Find(&todos)
if (len(todos) <= 0) {
c.JSON(http.StatusNotFound, gin.H{"status" : http.StatusNotFound, "message" : "No todo found!"})
return
}
//transforms the todos for building a good response,
//je peux choisir des champs a ne pas display
for _, item := range todos {
status := false
if (item.Status == 1) {
status = true
} else {
status = false
}
_todos = append(_todos, TransformedTodo{ID: item.ID, URL:item.URL})
}
c.JSON(http.StatusOK, gin.H{"status" : http.StatusOK, "data" : _todos})
}
//FetchSingleTodo Récupération d'un seul todo en fonction de son id
func FetchSingleTodo(c *gin.Context) {
var todo Todo
todoID := c.Param("id")
db, _ := Database()
defer db.Close()
db.First(&todo, todoID)
if (todo.ID == 0) {
c.JSON(http.StatusNotFound, gin.H{"status" : http.StatusNotFound, "message" : "No todo found!"})
return
}
_todo := TransformedTodo{ID: todo.ID, URL:todo.URL}
c.JSON(http.StatusOK, gin.H{"status" : http.StatusOK, "data" : _todo})
}
//UpdateTodo Mise à jour d'un todo
func UpdateTodo(c *gin.Context) {
var todo Todo
todoID := c.Param("id")
db, _ := Database()
defer db.Close()
db.First(&todo, todoID)
if (todo.ID == 0) {
c.JSON(http.StatusNotFound, gin.H{"status" : http.StatusNotFound, "message" : "No todo found!"})
return
}
db.Model(&todo).Update("title", c.PostForm("title"))
db.Model(&todo).Update("completed", c.PostForm("completed"))
c.JSON(http.StatusOK, gin.H{"status" : http.StatusOK, "message" : "Todo updated successfully!"})
}
//DeleteTodo Suppression d'un todo
func DeleteTodo(c *gin.Context) {
var todo Todo
todoID := c.Param("id")
db, _ := Database()
defer db.Close()
db.First(&todo, todoID)
if (todo.ID == 0) {
c.JSON(http.StatusNotFound, gin.H{"status" : http.StatusNotFound, "message" : "No todo found!"})
return
}
db.Delete(&todo)
c.JSON(http.StatusOK, gin.H{"status" : http.StatusOK, "message" : "Todo deleted successfully!"})
}
The Idea is to have a xxxxCRUD.go file for each entity to handle but the whole folder in the same package.
the exact errror is :
> PS D:\www\developpement> go run .\maincode.go [GIN-debug] [WARNING]
> Running in "debug" mode. Switch to "release" mode in production.
> - using env: export GIN_MODE=release
> - using code: gin.SetMode(gin.ReleaseMode)
>
> panic: runtime error: invalid memory address or nil pointer
> dereference [signal 0xc0000005 code=0x0 addr=0x0 pc=0x46cc0f]
>
> goroutine 1 [running]: panic(0x831880, 0xc04200a080)
> C:/Go/src/runtime/panic.go:500 +0x1af
> /D/www/developpement/apirest.LoadTodo()
> D:/www/developpement/apirest/todoCRUD.go:33 +0x2f [Router.Group]
> /D/www/developpement/apirest.init.1()
> D:/www/developpement/apirest/apirest.go:73 +0x220 [LoadTodo()]
> /D/www/developpement/apirest.init()
> D:/www/developpement/apirest/todoCRUD.go:190 +0x80 [last line] main.init()
> D:/www/developpement/maincode.go:13 +0x3a [manners.ListenAndServe(":8080", apirest.Router)] exit status 2
I hope that this will help to understand !
Thanks for your time and help !
Stéphane
答案1
得分: 1
我设法通过在LoadTodo函数中“发送”路由器来使其工作:
在apirest.go中:
LoadTodo(Router)
在todoCRUD.go中:
func LoadTodo(r *gin.Engine){
它正常工作...我希望我没有在代码中引入错误故障...
Stéphane
英文:
I did manage to make it work by "sending" the router in the LoadTodo function :
in apirest.go :
LoadTodo(Router)
and in todoCRUD.go :
func LoadTodo(r *gin.Engine){
and it's working fine ... I hope I didn't bring a bug failure in the code ...
Stéphane
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论