181

Question: Currently I'm printing out my response in the func Index like this fmt.Fprintf(w, string(response)) however, how can I send JSON properly in the request so that it maybe consumed by a view?

package main import ( "fmt" "github.com/julienschmidt/httprouter" "net/http" "log" "encoding/json" ) type Payload struct { Stuff Data } type Data struct { Fruit Fruits Veggies Vegetables } type Fruits map[string]int type Vegetables map[string]int func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { response, err := getJsonResponse(); if err != nil { panic(err) } fmt.Fprintf(w, string(response)) } func main() { router := httprouter.New() router.GET("/", Index) log.Fatal(http.ListenAndServe(":8080", router)) } func getJsonResponse()([]byte, error) { fruits := make(map[string]int) fruits["Apples"] = 25 fruits["Oranges"] = 10 vegetables := make(map[string]int) vegetables["Carrats"] = 10 vegetables["Beets"] = 0 d := Data{fruits, vegetables} p := Payload{d} return json.MarshalIndent(p, "", " ") } 
1

6 Answers 6

239

You can set your content-type header so clients know to expect json

w.Header().Set("Content-Type", "application/json") 

Another way to marshal a struct to json is to build an encoder using the http.ResponseWriter

// get a payload p := Payload{d} json.NewEncoder(w).Encode(p) 
Sign up to request clarification or add additional context in comments.

8 Comments

While w.Header().Set("Content-Type", "application/json") is correct for setting the content type, it doesn't when using json.NewEncoder instead i get a txt/plain result. Is anyone else getting this. The answer from @poorva worked as expected
Scratch that. If i use w.WriteHeader(http.StatusOk) I get the above result.
If I use w.WriteHeader(http.StatusOk) then I get text/plain; charset=utf-8, if I dont set the Status-Code explicitly I get applicaton/json and the Response has still a Status-Code 200.
Hmmm ... could it have to do with the docs here? Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
Adding w.Header().Set("Content-Type", "application/json") above json.NewEncoder(w).Encode(p) work for me
|
88

Other users were commenting that the Content-Type is plain/text when encoding.
You have to set the content type with w.Header().Set() first, then write the HTTP response code with w.WriteHeader().

If you call w.WriteHeader() first, then call w.Header().Set() after you will get plain/text.

An example handler might look like this:

func SomeHandler(w http.ResponseWriter, r *http.Request) { data := SomeStruct{} w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(data) } 

1 Comment

How to return response , if my program panics ? I tried using recover() , then returning from their but it didn't work out.
54

You can do something like this in you getJsonResponse function -

jData, err := json.Marshal(Data) if err != nil { // handle error } w.Header().Set("Content-Type", "application/json") w.Write(jData) 

6 Comments

One important note about this version is that it uses a byte slice in jData, possibly unnecessarily. Data can be of arbitrary size, depending on the data being marshalled, so this could be a non-trivial memory waster. After marshalling, we copy from memory to the ResponseWriter stream. The answer that uses json.NewEncoder() etc. would write the marshalled JSON straight into the ResponseWriter (into its stream ..)
Worked for me! Faced the issue when 'w.WriteHeader(http.StatusCreated)' was added before or after.
No need to return after panic as this exits your program
At least this solution doesn't add the trailing \n of the Encoder.Encode() function
@Jonno you are right, but it's the only answer when you can check the encode goes well BEFORE writing the header, because once written it can be changed!
|
3

In gobuffalo.io framework I got it to work like this:

// say we are in some resource Show action // some code is omitted user := &models.User{} if c.Request().Header.Get("Content-type") == "application/json" { return c.Render(200, r.JSON(user)) } else { // Make user available inside the html template c.Set("user", user) return c.Render(200, r.HTML("users/show.html")) } 

and then when I want to get JSON response for that resource I have to set "Content-type" to "application/json" and it works.

I think Rails has more convenient way to handle multiple response types, I didn't see the same in gobuffalo so far.

Comments

1

You may use this package renderer, I have written to solve this kind of problem, it's a wrapper to serve JSON, JSONP, XML, HTML etc.

Comments

0

This is a complement answer with a proper example:

func (ch captureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPost: body, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, fmt.Sprintf("error reading request body, %v", err), http.StatusInternalServerError) return } ...do your stuff here... case http.MethodGet: w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode( ...put your object here...) if err != nil { http.Error(w, fmt.Sprintf("error building the response, %v", err), http.StatusInternalServerError) return } default: http.Error(w, fmt.Sprintf("method %s is not allowed", r.Method), http.StatusMethodNotAllowed) } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.