由于我是golang的新手,有人可以在這里幫助我嗎?我有一個看起來像這樣的 yaml 檔案:
port: 5000
handlers:
- name: test1
uri: /api/test1
response:
status: 200
body: test1
- name: test2
uri: /api/test2
response:
status: 500
body: test2
基于這個檔案我想創建一個服務器。目前我正在嘗試這樣做,但看起來它沒有按預期作業。我做錯了什么,實作我需要的更好方法是什么?
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"gopkg.in/yaml.v2"
)
func main() {
config := parseYaml("conf.yaml")
configHandlers := config.Handlers
mux := http.NewServeMux()
for _, handler := range *configHandlers {
mux.HandleFunc(*handler.Uri, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(*handler.Response.Status)
fmt.Fprintf(w, *handler.Response.Body)
})
}
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", *config.Port), mux))
}
type YamlConfig struct {
Port *string `yaml:"port"`
Handlers *[]HandlerConfig `yaml:"handlers"`
}
type HandlerConfig struct {
Uri *string `yaml:"uri"`
Name *string `yaml:"name"`
Response *Response `yaml:"response"`
}
type Response struct {
Status *int `yaml:"status"`
Body *string `yaml:"body"`
}
func (c *YamlConfig) parseYaml(data []byte) error {
return yaml.Unmarshal(data, c)
}
func parseYaml(path string) YamlConfig {
data, err := ioutil.ReadFile(path)
if err != nil {
log.Fatal(err)
}
var config YamlConfig
if err := config.parseYaml(data); err != nil {
log.Fatal(err)
}
return config
}
更新:如果我運行此服務器,那么無論我點擊哪個端點,它都會回傳我500并test2在正文中
uj5u.com熱心網友回復:
您所看到的似乎是人們常見的陷阱:
configHandlers := config.Handlers
mux := http.NewServeMux()
for _, handler := range *configHandlers {
mux.HandleFunc(*handler.Uri, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(*handler.Response.Status)
fmt.Fprintf(w, *handler.Response.Body)
})
}
for回圈在每次迭代中重新分配變數handler。在回圈體中,您創建一個新函式并將其傳遞給mux.HandlerFun. 這些函式體有點繼承外部范圍,并訪問這個handler變數。變數在函式之外被重新分配,因此每個處理函式可以訪問的值隨之發生變化。你可以做些什么來解決這個問題是屏蔽handler回圈使用的變數,并創建一個對每個處理程式都是唯一的范圍。像 JavaScript 這樣的語言的經典方法(這是 - 或者當我寫一些 JS 時曾經回來 - 一個常見問題)是將代碼包裝在 IIFE(立即呼叫函式運算式)中:
for _, handler := range *configHandlers {
func (handler *HandlerConfig) { // handler is now the argument passed to this function
mux.HandleFunc(*handler.Uri, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(*handler.Response.Status)
fmt.Fprintf(w, *handler.Response.Body)
})
}(handler) // call the function with the _current_ value of handler
}
這有點混亂,因為 golang 是正確的塊作用域,你可以這樣做:
for _, handler := range *configHandlers {
h := handler // create a variable in the inner scope
mux.HandleFunc(*handler.Uri, func(w http.ResponseWriter, r *http.Request) {
// now h will reference a copy unique to each iteration
w.WriteHeader(*h.Response.Status)
fmt.Fprintf(w, *h.Response.Body)
})
}
那應該可以解決它。不過,我注意到您在添加到問題中的型別中使用指標有些奇怪……欄位之類Port的 type *string?你為什么不直接使用string?Body型別中的和Status欄位不一樣Response。通過將它們更改為普通string 欄位,您不必在處理程式函式中取消參考它們。看起來會干凈很多。
更大的擔憂是這個領域:
Handlers *[]HandlerConfig `yaml:"handlers"`
我不確定你是否真的知道這個欄位的型別,但這幾乎沒有意義。Handlers現在是一個指向HandlerConfig值切片的指標。我假設您希望此欄位為:
// Handlers is a slice of HandlerConfig values:
Handlers []HandlerConfig `yaml:"handlers"`
// or Handlers is a slice of pointers to HandlerConfig values
Handlers []*HandlerConfig `yaml:"handlers"`
一般來說,指向切片的指標,尤其是在配置型別中是錯誤代碼。
uj5u.com熱心網友回復:
如果您定義一個結構來表示 YAML 檔案中的配置,您可以使用yaml包將 yaml 解組為該型別的實體化結構。從那里,您可以像參考任何其他結構一樣參考結構中的欄位。
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
type YamlExample struct {
FieldOne string `yaml:"fieldOne"`
NestedField struct {
Name string `yaml:"name"`
} `yaml:"nestedField"`
}
const YamlEx string = `
fieldOne: one
nestedField:
name: nestedFieldName
`
func main() {
var yamlE YamlExample
err := yaml.Unmarshal([]byte(YamlEx), &yamlE)
if err != nil {
panic(err)
}
fmt.Printf("% v\n", yamlE)
}
鏈接到示例。
在您的情況下,您可能希望處理結構中的路由,然后參考結構中的欄位以獲取路由名稱、如何處理請求正文等內容。如果您的 YAML 存盤在檔案中,您必須使用類似io包的東西將檔案讀入 YAML 包可以決議的位元組陣列。 請參閱此處以獲取參考。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/466880.html
上一篇:如何指示PowerShell7在Windows10上的WSL2中運行bash(Ubuntu)腳本?
下一篇:不在主包中的Go檔案未運行
