lhk
lhk

Reputation: 30006

sending JSON with 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 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

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

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.

Upvotes: 2

Views: 2750

Answers (2)

GeertJohan
GeertJohan

Reputation: 425

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

Upvotes: 5

Nick Craig-Wood
Nick Craig-Wood

Reputation: 54081

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!

Upvotes: 3

Related Questions