Array of structures outside main – Go

huangapple go评论117阅读模式
英文:

Array of structures outside main - Go

问题

我正在使用一个REST框架(https://github.com/ant0ine/go-json-rest)进行开发,并且我正在尝试存储一个对象数组。

  1. type Item struct {
  2. Name string
  3. }
  4. // 我想创建一个Item数组
  5. func Add(w *rest.ResponseWriter, req *rest.Request) {
  6. data := Item{}
  7. err := req.DecodeJsonPayload(&data)
  8. if err != nil {
  9. rest.Error(w, err.Error(), http.StatusInternalServerError)
  10. return
  11. }
  12. // 我想将新的Item对象添加到Item数组中
  13. w.WriteJson(&data)
  14. }
  15. func main() {
  16. handler := rest.ResourceHandler{
  17. EnableRelaxedContentType: true,
  18. }
  19. handler.SetRoutes(
  20. rest.Route{"POST", "/add", Add},
  21. )
  22. http.ListenAndServe(":8080", &handler)
  23. }

我不确定如何在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.

  1. type Item struct {
  2. Name string
  3. }
  4. // I want to create an array of Items
  5. func Add(w *rest.ResponseWriter, req *rest.Request) {
  6. data := Item{}
  7. err := req.DecodeJsonPayload(&data)
  8. if err != nil {
  9. rest.Error(w, err.Error(), http.StatusInternalServerError)
  10. return
  11. }
  12. // I want to append the new Item object to the array of items
  13. w.WriteJson(&data)
  14. }
  15. func main() {
  16. handler := rest.ResourceHandler{
  17. EnableRelaxedContentType: true,
  18. }
  19. handler.SetRoutes(
  20. rest.Route{"POST", "/add", Add},
  21. )
  22. http.ListenAndServe(":8080", &handler)
  23. }

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

你可以在本地定义一个持有数据的结构:

  1. type ItemHolder struct {
  2. items []Item
  3. Input chan Item
  4. Request chan ItemRequest
  5. }

还有一个接受新数据或回答当前数据请求的goroutine:

  1. func (i *ItemHolder) Run() {
  2. for {
  3. select {
  4. case req := <-i.Request:
  5. req.Items <- i.items
  6. case in := <-i.Input:
  7. i.items = append(i.items, in)
  8. }
  9. }
  10. }

你可以将其实例化为全局变量,因为这样是安全的:

  1. var itemHolder = &ItemHolder{
  2. Request: make(chan ItemRequest),
  3. Input: make(chan Item),
  4. }

使用起来很简单,将新的项放入itemHolderInput通道中:

  1. func Add(i int) {
  2. var item Item
  3. // 填充item的代码
  4. itemHolder.Input <- item
  5. }

请求当前项的状态意味着给itemHolder一个通道,用于将当前项放入其中:

  1. func PrintCurrentItems() {
  2. rchan := make(chan []Item)
  3. itemHolder.Request <- ItemRequest{rchan}
  4. fmt.Println("current items:", <-rchan)
  5. }

显然,你需要在某个时候启动itemHolder

  1. func main() {
  2. go itemHolder.Run()
  3. ListenAndServe(/* ... */)
  4. }

这样,你就可以安全地存储并发访问的项,同时仍然能够全局访问它们。

英文:

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:

  1. type ItemHolder struct {
  2. items []Item
  3. Input chan Item
  4. Request chan ItemRequest
  5. }

and a goroutine which accepts new data or answers requests for insight of the current data:

  1. func (i *ItemHolder) Run() {
  2. for {
  3. select {
  4. case req := &lt;-i.Request:
  5. eq.Items &lt;- i.items
  6. case in := &lt;-i.Input:
  7. i.items = append(i.items, in)
  8. }
  9. }
  10. }

This you would instantiate as a global variable since it is safe to do so:

  1. var itemHolder = &amp;ItemHolder{
  2. Request: make(chan ItemRequest),
  3. Input: make(chan Item),
  4. }

Usage is straight forward, putting new things in equals putting a value in the Input channel
of the itemHolder:

  1. func Add(i int) {
  2. var i Item
  3. // Your code filling i
  4. itemHolder.Input &lt;- i
  5. }

Requesting the current state of items means giving the itemHolder a channel to put the current
items in to.

  1. func PrintCurrentItems() {
  2. rchan := make(chan []Item)
  3. itemHolder.Request &lt;- ItemRequest{rchan}
  4. fmt.Println(&quot;current items:&quot;, &lt;-rchan)
  5. }

Obviously at some point you have to start the itemHolder:

  1. func main() {
  2. go itemHolder.Run()
  3. ListenAndServe(/* ... */)
  4. }

This way you have stored your items safe for concurrent access but are still able to access them globally.

答案2

得分: 1

这是我翻译好的内容:

我不确定这是否是最好的方法,但这个方法有效。

  1. type ItemSet struct {
  2. Items []*Item
  3. }
  4. var store ItemSet
  5. ..
  6. store.Items = append(store.Items, &data)
  7. ...
英文:

I'm not sure if this is the best way to do it but this works

  1. type ItemSet struct {
  2. Items []*Item
  3. }
  4. var store ItemSet
  5. ..
  6. store.Items = append(store.Items, &amp;data)
  7. ...

答案3

得分: 1

每个HTTP请求在自己的Go协程中运行。如果你想将项目存储在全局数据结构中,那么你需要使用锁来保护访问。

以下是文档中相关的示例代码:

  1. package main
  2. import (
  3. "github.com/ant0ine/go-json-rest/rest"
  4. "log"
  5. "net/http"
  6. "sync"
  7. )
  8. func main() {
  9. api := rest.NewApi()
  10. api.Use(rest.DefaultDevStack...)
  11. router, err := rest.MakeRouter(
  12. rest.Get("/countries", GetAllCountries),
  13. rest.Post("/countries", PostCountry),
  14. rest.Get("/countries/:code", GetCountry),
  15. rest.Delete("/countries/:code", DeleteCountry),
  16. )
  17. if err != nil {
  18. log.Fatal(err)
  19. }
  20. api.SetApp(router)
  21. log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
  22. }
  23. type Country struct {
  24. Code string
  25. Name string
  26. }
  27. var store = map[string]*Country{}
  28. var lock = sync.RWMutex{}
  29. func GetCountry(w rest.ResponseWriter, r *rest.Request) {
  30. code := r.PathParam("code")
  31. lock.RLock()
  32. var country *Country
  33. if store[code] != nil {
  34. country = &Country{}
  35. *country = *store[code]
  36. }
  37. lock.RUnlock()
  38. if country == nil {
  39. rest.NotFound(w, r)
  40. return
  41. }
  42. w.WriteJson(country)
  43. }
  44. func GetAllCountries(w rest.ResponseWriter, r *rest.Request) {
  45. lock.RLock()
  46. countries := make([]Country, len(store))
  47. i := 0
  48. for _, country := range store {
  49. countries[i] = *country
  50. i++
  51. }
  52. lock.RUnlock()
  53. w.WriteJson(&countries)
  54. }
  55. func PostCountry(w rest.ResponseWriter, r *rest.Request) {
  56. country := Country{}
  57. err := r.DecodeJsonPayload(&country)
  58. if err != nil {
  59. rest.Error(w, err.Error(), http.StatusInternalServerError)
  60. return
  61. }
  62. if country.Code == "" {
  63. rest.Error(w, "country code required", 400)
  64. return
  65. }
  66. if country.Name == "" {
  67. rest.Error(w, "country name required", 400)
  68. return
  69. }
  70. lock.Lock()
  71. store[country.Code] = &country
  72. lock.Unlock()
  73. w.WriteJson(&country)
  74. }
  75. func DeleteCountry(w rest.ResponseWriter, r *rest.Request) {
  76. code := r.PathParam("code")
  77. lock.Lock()
  78. delete(store, code)
  79. lock.Unlock()
  80. w.WriteHeader(http.StatusOK)
  81. }

另外,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:

  1. package main
  2. import (
  3. &quot;github.com/ant0ine/go-json-rest/rest&quot;
  4. &quot;log&quot;
  5. &quot;net/http&quot;
  6. &quot;sync&quot;
  7. )
  8. func main() {
  9. api := rest.NewApi()
  10. api.Use(rest.DefaultDevStack...)
  11. router, err := rest.MakeRouter(
  12. rest.Get(&quot;/countries&quot;, GetAllCountries),
  13. rest.Post(&quot;/countries&quot;, PostCountry),
  14. rest.Get(&quot;/countries/:code&quot;, GetCountry),
  15. rest.Delete(&quot;/countries/:code&quot;, DeleteCountry),
  16. )
  17. if err != nil {
  18. log.Fatal(err)
  19. }
  20. api.SetApp(router)
  21. log.Fatal(http.ListenAndServe(&quot;:8080&quot;, api.MakeHandler()))
  22. }
  23. type Country struct {
  24. Code string
  25. Name string
  26. }
  27. var store = map[string]*Country{}
  28. var lock = sync.RWMutex{}
  29. func GetCountry(w rest.ResponseWriter, r *rest.Request) {
  30. code := r.PathParam(&quot;code&quot;)
  31. lock.RLock()
  32. var country *Country
  33. if store[code] != nil {
  34. country = &amp;Country{}
  35. *country = *store[code]
  36. }
  37. lock.RUnlock()
  38. if country == nil {
  39. rest.NotFound(w, r)
  40. return
  41. }
  42. w.WriteJson(country)
  43. }
  44. func GetAllCountries(w rest.ResponseWriter, r *rest.Request) {
  45. lock.RLock()
  46. countries := make([]Country, len(store))
  47. i := 0
  48. for _, country := range store {
  49. countries[i] = *country
  50. i++
  51. }
  52. lock.RUnlock()
  53. w.WriteJson(&amp;countries)
  54. }
  55. func PostCountry(w rest.ResponseWriter, r *rest.Request) {
  56. country := Country{}
  57. err := r.DecodeJsonPayload(&amp;country)
  58. if err != nil {
  59. rest.Error(w, err.Error(), http.StatusInternalServerError)
  60. return
  61. }
  62. if country.Code == &quot;&quot; {
  63. rest.Error(w, &quot;country code required&quot;, 400)
  64. return
  65. }
  66. if country.Name == &quot;&quot; {
  67. rest.Error(w, &quot;country name required&quot;, 400)
  68. return
  69. }
  70. lock.Lock()
  71. store[country.Code] = &amp;country
  72. lock.Unlock()
  73. w.WriteJson(&amp;country)
  74. }
  75. func DeleteCountry(w rest.ResponseWriter, r *rest.Request) {
  76. code := r.PathParam(&quot;code&quot;)
  77. lock.Lock()
  78. delete(store, code)
  79. lock.Unlock()
  80. w.WriteHeader(http.StatusOK)
  81. }

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.

huangapple
  • 本文由 发表于 2014年2月27日 10:25:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/22057813.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定