英文:
Array of structures outside main - Go
问题
我正在使用一个REST框架(https://github.com/ant0ine/go-json-rest)进行开发,并且我正在尝试存储一个对象数组。
type Item struct {
Name string
}
// 我想创建一个Item数组
func Add(w *rest.ResponseWriter, req *rest.Request) {
data := Item{}
err := req.DecodeJsonPayload(&data)
if err != nil {
rest.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 我想将新的Item对象添加到Item数组中
w.WriteJson(&data)
}
func main() {
handler := rest.ResourceHandler{
EnableRelaxedContentType: true,
}
handler.SetRoutes(
rest.Route{"POST", "/add", Add},
)
http.ListenAndServe(":8080", &handler)
}
我不确定如何在main()之外实例化一个Item数组。如果使用全局数组不是最佳实践,那么应该使用什么方法?该框架提供的示例中使用了全局映射,但在我的用例中,我不能使用唯一的键。
英文:
I've working with a REST framework (https://github.com/ant0ine/go-json-rest) and I'm trying to store an array of objects.
type Item struct {
Name string
}
// I want to create an array of Items
func Add(w *rest.ResponseWriter, req *rest.Request) {
data := Item{}
err := req.DecodeJsonPayload(&data)
if err != nil {
rest.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// I want to append the new Item object to the array of items
w.WriteJson(&data)
}
func main() {
handler := rest.ResourceHandler{
EnableRelaxedContentType: true,
}
handler.SetRoutes(
rest.Route{"POST", "/add", Add},
)
http.ListenAndServe(":8080", &handler)
}
I'm unsure as to how to instantiate an array of Items outside of main(). If using a global array is not the best practice, what would be it? The examples provided by the framework featured a global map but in my use case, I cannot have a unique key.
答案1
得分: 4
我不建议使用可变的全局数组,因为Add
可能会被你的REST API包并发调用,因此对全局数组的访问必须进行同步。
你可以编写一个处理项的例程,并通过通道进行通信(示例见play)。
你可以在本地定义一个持有数据的结构:
type ItemHolder struct {
items []Item
Input chan Item
Request chan ItemRequest
}
还有一个接受新数据或回答当前数据请求的goroutine:
func (i *ItemHolder) Run() {
for {
select {
case req := <-i.Request:
req.Items <- i.items
case in := <-i.Input:
i.items = append(i.items, in)
}
}
}
你可以将其实例化为全局变量,因为这样是安全的:
var itemHolder = &ItemHolder{
Request: make(chan ItemRequest),
Input: make(chan Item),
}
使用起来很简单,将新的项放入itemHolder
的Input
通道中:
func Add(i int) {
var item Item
// 填充item的代码
itemHolder.Input <- item
}
请求当前项的状态意味着给itemHolder
一个通道,用于将当前项放入其中:
func PrintCurrentItems() {
rchan := make(chan []Item)
itemHolder.Request <- ItemRequest{rchan}
fmt.Println("current items:", <-rchan)
}
显然,你需要在某个时候启动itemHolder
:
func main() {
go itemHolder.Run()
ListenAndServe(/* ... */)
}
这样,你就可以安全地存储并发访问的项,同时仍然能够全局访问它们。
英文:
I wouldn't recommend using a mutable global array as Add
is probably called concurrently by your REST API package and therefore access to the global array must be synchronized.
What you could do is to write a routine that handles the items for you and communicates via a channel (example on play).
You would have the structure holding your data locally:
type ItemHolder struct {
items []Item
Input chan Item
Request chan ItemRequest
}
and a goroutine which accepts new data or answers requests for insight of the current data:
func (i *ItemHolder) Run() {
for {
select {
case req := <-i.Request:
eq.Items <- i.items
case in := <-i.Input:
i.items = append(i.items, in)
}
}
}
This you would instantiate as a global variable since it is safe to do so:
var itemHolder = &ItemHolder{
Request: make(chan ItemRequest),
Input: make(chan Item),
}
Usage is straight forward, putting new things in equals putting a value in the Input
channel
of the itemHolder
:
func Add(i int) {
var i Item
// Your code filling i
itemHolder.Input <- i
}
Requesting the current state of items means giving the itemHolder
a channel to put the current
items in to.
func PrintCurrentItems() {
rchan := make(chan []Item)
itemHolder.Request <- ItemRequest{rchan}
fmt.Println("current items:", <-rchan)
}
Obviously at some point you have to start the itemHolder
:
func main() {
go itemHolder.Run()
ListenAndServe(/* ... */)
}
This way you have stored your items safe for concurrent access but are still able to access them globally.
答案2
得分: 1
这是我翻译好的内容:
我不确定这是否是最好的方法,但这个方法有效。
type ItemSet struct {
Items []*Item
}
var store ItemSet
..
store.Items = append(store.Items, &data)
...
英文:
I'm not sure if this is the best way to do it but this works
type ItemSet struct {
Items []*Item
}
var store ItemSet
..
store.Items = append(store.Items, &data)
...
答案3
得分: 1
每个HTTP请求在自己的Go协程中运行。如果你想将项目存储在全局数据结构中,那么你需要使用锁来保护访问。
以下是文档中相关的示例代码:
package main
import (
"github.com/ant0ine/go-json-rest/rest"
"log"
"net/http"
"sync"
)
func main() {
api := rest.NewApi()
api.Use(rest.DefaultDevStack...)
router, err := rest.MakeRouter(
rest.Get("/countries", GetAllCountries),
rest.Post("/countries", PostCountry),
rest.Get("/countries/:code", GetCountry),
rest.Delete("/countries/:code", DeleteCountry),
)
if err != nil {
log.Fatal(err)
}
api.SetApp(router)
log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}
type Country struct {
Code string
Name string
}
var store = map[string]*Country{}
var lock = sync.RWMutex{}
func GetCountry(w rest.ResponseWriter, r *rest.Request) {
code := r.PathParam("code")
lock.RLock()
var country *Country
if store[code] != nil {
country = &Country{}
*country = *store[code]
}
lock.RUnlock()
if country == nil {
rest.NotFound(w, r)
return
}
w.WriteJson(country)
}
func GetAllCountries(w rest.ResponseWriter, r *rest.Request) {
lock.RLock()
countries := make([]Country, len(store))
i := 0
for _, country := range store {
countries[i] = *country
i++
}
lock.RUnlock()
w.WriteJson(&countries)
}
func PostCountry(w rest.ResponseWriter, r *rest.Request) {
country := Country{}
err := r.DecodeJsonPayload(&country)
if err != nil {
rest.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if country.Code == "" {
rest.Error(w, "country code required", 400)
return
}
if country.Name == "" {
rest.Error(w, "country name required", 400)
return
}
lock.Lock()
store[country.Code] = &country
lock.Unlock()
w.WriteJson(&country)
}
func DeleteCountry(w rest.ResponseWriter, r *rest.Request) {
code := r.PathParam("code")
lock.Lock()
delete(store, code)
lock.Unlock()
w.WriteHeader(http.StatusOK)
}
另外,ResourceHandler
已被弃用,你可能想使用v3 API。
Antoine - go-json-rest作者
英文:
Each HTTP request runs in its own Go Routine. If you want to store the items in a global data structure then you'll have to protect the access using a lock.
Here is the relevant example from the documentation:
package main
import (
"github.com/ant0ine/go-json-rest/rest"
"log"
"net/http"
"sync"
)
func main() {
api := rest.NewApi()
api.Use(rest.DefaultDevStack...)
router, err := rest.MakeRouter(
rest.Get("/countries", GetAllCountries),
rest.Post("/countries", PostCountry),
rest.Get("/countries/:code", GetCountry),
rest.Delete("/countries/:code", DeleteCountry),
)
if err != nil {
log.Fatal(err)
}
api.SetApp(router)
log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}
type Country struct {
Code string
Name string
}
var store = map[string]*Country{}
var lock = sync.RWMutex{}
func GetCountry(w rest.ResponseWriter, r *rest.Request) {
code := r.PathParam("code")
lock.RLock()
var country *Country
if store[code] != nil {
country = &Country{}
*country = *store[code]
}
lock.RUnlock()
if country == nil {
rest.NotFound(w, r)
return
}
w.WriteJson(country)
}
func GetAllCountries(w rest.ResponseWriter, r *rest.Request) {
lock.RLock()
countries := make([]Country, len(store))
i := 0
for _, country := range store {
countries[i] = *country
i++
}
lock.RUnlock()
w.WriteJson(&countries)
}
func PostCountry(w rest.ResponseWriter, r *rest.Request) {
country := Country{}
err := r.DecodeJsonPayload(&country)
if err != nil {
rest.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if country.Code == "" {
rest.Error(w, "country code required", 400)
return
}
if country.Name == "" {
rest.Error(w, "country name required", 400)
return
}
lock.Lock()
store[country.Code] = &country
lock.Unlock()
w.WriteJson(&country)
}
func DeleteCountry(w rest.ResponseWriter, r *rest.Request) {
code := r.PathParam("code")
lock.Lock()
delete(store, code)
lock.Unlock()
w.WriteHeader(http.StatusOK)
}
Also 'ResourceHandler' is deprecated, you may want to use the v3 API.
Antoine - go-json-rest author
答案4
得分: 0
你可以使用Init函数,它通过func init() {...}
调用。根据文档的说明:
“每个源文件都可以定义自己的无参数init函数来设置所需的任何状态。(实际上,每个文件可以有多个init函数。)而finally的意思是最后:init在包中的所有变量声明都已经评估其初始化器之后调用,而这些初始化器只有在所有导入的包都被初始化之后才会被评估。
除了无法表示为声明的初始化之外,init函数的常见用途是在真正的执行开始之前验证或修复程序状态的正确性。”
英文:
You could use The Init function, which is called with func init() {...}
. It is a function that is called before main()
and according to the docs:
Finally, each source file can define its own niladic init function to set up whatever state is required. (Actually each file can have multiple init functions.) And finally means finally: init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.
Besides initializations that cannot be expressed as declarations, a common use of init functions is to verify or repair correctness of the program state before real execution begins.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论