使用Go发送JSON

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

sending JSON with go

问题

我正在尝试使用Go发送JSON消息。
这是服务器代码:

func (network *Network) Join(
	w http.ResponseWriter,
	r *http.Request) {
	//请求不重要
	//响应将是一个只设置了clientId值的消息
	log.Println("客户端想要加入")
	message := Message{-1, -1, -1, ClientId(len(network.Clients)), -1, -1}
	var buffer bytes.Buffer
	enc := json.NewEncoder(&buffer)

	err := enc.Encode(message)
	if err != nil {
		fmt.Println("编码加入请求的响应时出错")
		log.Fatal(err)
	}

	fmt.Printf("json:%s\n", buffer.Bytes())
	fmt.Fprint(w, buffer.Bytes())
}

Network是一个自定义结构。在main函数中,我创建了一个network对象,并将其方法注册为http.HandleFunc(...)的回调函数。

func main() {
	runtime.GOMAXPROCS(2)
	var network = new(Network)
	var clients = make([]Client, 0, 10)
	network.Clients = clients

	log.Println("启动服务器")
	http.HandleFunc("/request", network.Request)
	http.HandleFunc("/update", network.GetNews)
	http.HandleFunc("/join", network.Join)
	log.Fatal(http.ListenAndServe("localhost:5000", nil))
}

Message也是一个结构体。它有六个字段,都是int类型的别名。
当客户端向URL“localhost:5000/join”发送HTTP GET请求时,应该发生以下情况:

  • 调用network对象上的Join方法
  • 创建一个带有客户端ID的新Message对象
  • 将此Message编码为JSON
  • 为了检查编码是否正确,将编码后的消息打印在cmd上
  • 将消息写入ResponseWriter

客户端非常简单。它的Message结构体代码完全相同。在main函数中,它只是向“localhost:5000/join”发送一个GET请求,并尝试解码响应。以下是代码:

func main() {

	//尝试加入
	var clientId ClientId
	start := time.Now()
	var message Message
	resp, err := http.Get("http://localhost:5000/join")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(resp.Status)
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&message)
	if err != nil {
		fmt.Println("解码加入请求的响应时出错")
		log.Fatal(err)
	}

	fmt.Println(message)
	duration := time.Since(start)
	fmt.Println("连接后:", duration)
	fmt.Println("使用clientId:", message.ClientId)
}

我启动了服务器,等待了几秒钟,然后运行了客户端。这是结果:

  • 服务器打印“客户端想要加入”
  • 服务器打印“json:{"What":-1,"Tag":-1,"Id":-1,"ClientId":0,"X":-1,"Y":-1}”
  • 客户端打印“200 OK”
  • 客户端崩溃“解码加入请求的响应时出错”
  • 错误消息是“数组元素后的无效字符“3””

这个错误消息真的让我困惑。毕竟,在我的JSON中,没有数字3。所以我在客户端导入了io/ioutil,并用以下代码打印了响应:

b, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("json:%s\n", b)

请注意,打印语句与服务器上的相同。我期望看到我的编码JSON。但是我得到了这个:

  • “200 OK”
  • “json:[123 34 87 104 97 116 ....]” 列表继续很长

我对Go还不熟悉,不知道我是否做得正确。但是似乎上面的代码只是打印了字节切片。奇怪的是,在服务器上,输出被转换为字符串。

我猜想我可能读取了错误的数据,或者消息在服务器和客户端之间的传输过程中被损坏。但是老实说,这只是一些猜测。

英文:

I'm trying to send a JSON message with Go.
This is the server code:

func (network *Network) Join(
	w http.ResponseWriter,
	r *http.Request) {
	//the request is not interesting
	//the response will be a message with just the clientId value set
	log.Println("client wants to join")
	message := Message{-1, -1, -1, ClientId(len(network.Clients)), -1, -1}
	var buffer bytes.Buffer
	enc := json.NewEncoder(&buffer)

	err := enc.Encode(message)
	if err != nil {
		fmt.Println("error encoding the response to a join request")
		log.Fatal(err)
	}

	fmt.Printf("the json: %s\n", buffer.Bytes())
	fmt.Fprint(w, buffer.Bytes())
}

Network is a custom struct. In the main function, I'm creating a network object and registering it's methods as callbacks to http.HandleFunc(...)

func main() {
	runtime.GOMAXPROCS(2)
	var network = new(Network)
	var clients = make([]Client, 0, 10)
	network.Clients = clients

	log.Println("starting the server")
	http.HandleFunc("/request", network.Request)
	http.HandleFunc("/update", network.GetNews)
	http.HandleFunc("/join", network.Join)
	log.Fatal(http.ListenAndServe("localhost:5000", nil))
}

Message is a struct, too. It has six fields all of a type alias for int.
When a client sends an http GET request to the url "localhost:5000/join", this should happen

  • The method Join on the network object is called
  • A new Message object with an Id for the client is created
  • This Message is encoded as JSON
  • To check if the encoding is correct, the encoded message is printed on the cmd
  • The message is written to the ResponseWriter

The client is rather simple. It has the exact same code for the Message struct. In the main function it just sends a GET request to "localhost:5000/join" and tries to decode the response. Here's the code

func main() {

	// try to join
	var clientId ClientId
	start := time.Now()
	var message Message
	resp, err := http.Get("http://localhost:5000/join")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(resp.Status)
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&message)
	if err != nil {
		fmt.Println("error decoding the response to the join request")
		log.Fatal(err)
	}

	fmt.Println(message)
	duration := time.Since(start)
	fmt.Println("connected after: ", duration)
	fmt.Println("with clientId", message.ClientId)
}

I've started the server, waited a few seconds and then ran the client. This is the result

  • The server prints "client wants to join"
  • The server prints "the json: {"What":-1,"Tag":-1,"Id":-1,"ClientId":0,"X":-1,"Y":-1}"
  • The client prints "200 OK"
  • The client crashes "error decoding the response to the join request"
  • The error is "invalid character "3" after array element"

This error message really confused me. After all, nowhere in my json, there's the number 3. So I imported io/ioutil on the client and just printed the response with this code

b, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("the json: %s\n", b)

Please note that the print statement is the same as on the server. I expected to see my encoded JSON. Instead I got this

  • "200 OK"
  • "the json: [123 34 87 104 97 116 ....]" the list went on for a long time

I'm new to go and don't know if i did this correctly. But it seems as if the above code just printed the slice of bytes. Strange, on the server the output was converted to a string.

My guess is that somehow I'm reading the wrong data or that the message was corrupted on the way between server and client. But honestly these are just wild guesses.

答案1

得分: 5

在你的服务器中,不要使用

fmt.Fprint(w, buffer.Bytes())

而是需要使用:

w.Write(buffer.Bytes())

fmt包会将Bytes()格式化为一个可读的切片,其中字节被表示为整数,例如:

[123 34 87 104 97 116 ... 等等
英文:

In your server, instead of

fmt.Fprint(w, buffer.Bytes())

you need to use:

w.Write(buffer.Bytes())

The fmt package will format the Bytes() into a human-readable slice with the bytes represented as integers, like so:

[123 34 87 104 97 116 ... etc

答案2

得分: 3

你不想使用fmt.Print将内容写入响应中。例如:

package main

import (
    "fmt"
    "os"
)

func main() {
    bs := []byte("Hello, playground")
    fmt.Fprint(os.Stdout, bs)
}

playground链接

输出结果为:

[72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100]

应该使用ResponseWriter的Write()方法来代替。

当你不确定发生了什么时,通过telnet连接到服务器进行实验是一个好主意!

英文:

You don't want to use fmt.Print to write stuff to the response. Eg

package main

import (
	"fmt"
	"os"
)

func main() {
	bs := []byte("Hello, playground")
	fmt.Fprint(os.Stdout, bs)
}

(playground link)

Produces

[72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100]

Use the Write() method of the ResponseWriter instead

You could have found this out by telneting to your server as an experiment - always a good idea when you aren't sure what is going on!

huangapple
  • 本文由 发表于 2013年5月19日 20:19:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/16634582.html
匿名

发表评论

匿名网友

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

确定