主要以外的结构数组 - Go

时间:2014-02-27 02:25:30

标签: go

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

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

我不确定如何在main()之外实例化一个Items数组。如果使用全局数组不是最佳实践,那会是什么?框架提供的示例包含全局地图,但在我的用例中,我没有唯一的密钥。

4 个答案:

答案 0 :(得分:4)

我不建议使用可变全局数组,因为Add可能是由R​​EST API包同时调用的,因此必须同步对全局数组的访问。

您可以做的是编写一个例程,为您处理项目并通过渠道(example on play)进行沟通。

您将拥有在本地保存数据的结构:

type ItemHolder struct {
    items   []Item
    Input   chan Item
    Request chan ItemRequest
}

和goroutine接受新数据或回答对当前数据的洞察请求:

func (i *ItemHolder) Run() {
    for {
        select {
        case req := <-i.Request:
            eq.Items <- i.items
        case in := <-i.Input:
            i.items = append(i.items, in)
        }
    }
}

这将您实例化为全局变量,因为它是安全的:

var itemHolder = &ItemHolder{
    Request: make(chan ItemRequest),
    Input: make(chan Item),
}

直接使用,将新内容置于等于Input频道的值 itemHolder

func Add(i int) {
        var i Item
    // Your code filling i
    itemHolder.Input <- i
}

请求项目的当前状态意味着给itemHolder一个通道来放置当前状态 物品到。

func PrintCurrentItems() {
    rchan := make(chan []Item)
    itemHolder.Request <- ItemRequest{rchan}

    fmt.Println("current items:", <-rchan)
}

显然,在某些时候你必须开始itemHolder

func main() {
    go itemHolder.Run()

    ListenAndServe(/* ... */)
}

通过这种方式,您可以安全地存储项目以进行并发访问,但仍然可以全局访问它们。

答案 1 :(得分:1)

我不确定这是否是最好的方法,但这可行

type ItemSet struct {
    Items []*Item
}

var store ItemSet

..

store.Items = append(store.Items, &data)
...

答案 2 :(得分:1)

每个HTTP请求都在自己的Go Routine中运行。如果要将项目存储在全局数据结构中,则必须使用锁保护访问。

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

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

还有&#39; ResourceHandler&#39;不推荐使用,您可能需要使用v3 API。

Antoine - go-json-rest作者

答案 3 :(得分:0)

您可以使用The Init function来调用func init() {...}。这是一个在main()之前调用的函数,根据文档:

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.