0

In my application, when there is error I am writing a json error message on response body but this makes response code 200. I tried separately doing

json.NewEncoder(res).Encode(errorBody)
res.WriteHeader(http.StatusBadRequest)

But it still gives out response code 200 along with a warning that I am making multiple WriteHeader calls. I want to have something like

http.Error(res,"Some Error Message here",http.StatusBadRequest)

but instead of the error message in text format, I want it to be in JSON. What should I do?

Krash
  • 2,085
  • 3
  • 13
  • 36
  • 1
    Related: [Golang: terminating or aborting an HTTP request](http://stackoverflow.com/questions/31293314/golang-terminating-or-aborting-an-http-request/31293435#31293435); and [multiple response.WriteHeader calls in really simple example?](http://stackoverflow.com/questions/27972715/multiple-response-writeheader-calls-in-really-simple-example/27973060#27973060) – icza Apr 12 '17 at 09:21
  • 1
    `I am making multiple WriteHeader calls` -- this is your problem. For obvious reasons, only the first call to `WriteHeader` has any effect. Apparently, your first call is setting the status to 200. – Jonathan Hall Apr 12 '17 at 15:23
  • `but instead of the error message in text format, I want it to be in JSON. What should I do?` -- This is an unrelated question, and should be asked separately. – Jonathan Hall Apr 12 '17 at 15:24
  • @Flimzy Check mkopriva 's answer. He is also making similar calls but just in a different order. It doesn't give out a warning and works the way it is supposed to i.e. proper response code and then a json body on response body. How did changing the order make a difference? It is still making multiple calls – Krash Apr 12 '17 at 15:27
  • @Flimzy I added the `instead of the error message in text format, I want it to be in JSON.` part just to let everyone know how I have been doing it uptil now which made the response code appear properly. There could have been a similar approach for content type json, I dont know. I was just trying to put down as much info as I could to have my issue clarified. – Krash Apr 12 '17 at 15:30

2 Answers2

1

You can write the header first and then the body.

res.Header().Set("Content-Type", "application/json; charset=utf-8")
res.WriteHeader(http.StatusBadRequest)
if err = json.NewEncoder(res).Encode(err); err != nil {
    // handle encoder error
}

And if the response is still 200 and/or you're getting the warning, that means that you or some of the packages you use are writing the header for you in some other place.

Update: (to answer relevant question from comment)

json.NewEncoder(res).Encode(err) calls the Write method on res, and the implementation of Write from the standard net/http package automatically sets the status to 200 if it's not already set. And the implementation of WriteHeader checks whether status is already set or not and if it is, it just logs the warning and bails, without overwriting it.

So if you wan't to control the status you need to call WriteHeader before your write to the response whether you're writing json with the encoder or calling res.Write yourself.

mkopriva
  • 35,176
  • 4
  • 57
  • 71
  • Why did changing the order of the calls worked flawlessly? What was wrong with my way? – Krash Apr 12 '17 at 15:25
  • 1
    @Krash: The first time you write _anything_, `WriteHeader` is called (by default with status 200). [Read the docs](https://golang.org/pkg/net/http/#ResponseWriter) for more info. – Jonathan Hall Apr 12 '17 at 15:36
0

json format is still plain text. json.Marshal errorBody to string first.

body, err := json.Marshal(errBody)
if err != nil{
    // todo
}
http.Error(res, string(body), http.StatusBadRequest)
backing
  • 127
  • 4