user10714010
user10714010

Reputation: 935

Can you return json in golang http.Error?

Can you return json when http.Error is called?

        myObj := MyObj{
            MyVar: myVar}

        data, err := json.Marshal(myObj)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return 
        }
        w.Write(data)
        w.Header().Set("Content-Type", "application/json")

        http.Error(w, "some error happened", http.StatusInternalServerError)

I see that it returns 200 with no json but the json is embed in text

Upvotes: 7

Views: 15708

Answers (4)

kinshuk4
kinshuk4

Reputation: 3251

My answer is a bit late and there are some good answers already. Here are my 2 cents.

If you want to return JSON in case of error there are multiple ways to do so. I can list two:

  1. Write your own Error handler method
  2. Use the go-boom library

1. Writing your own error handler method

One way is what @craigmj has suggested, i.e. create your own method, for eg.:

func JSONError(w http.ResponseWriter, err interface{}, code int) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.WriteHeader(code)
    json.NewEncoder(w).Encode(err)
}

2. Use the go-boom library

Another approach is using the go-boom library. For eg., in case the err relates to resource not found, you can do:

err := errors.New("User doesn't exist")
...
boom.NotFound(w, err)

And the response will be:

{
  "error": "Not Found",
  "message": ",
  "statusCode": 404
}

For more check the documentation of the go-boom. Hope that helps.

Upvotes: 1

Shudipta Sharma
Shudipta Sharma

Reputation: 5882

Like @ShashankV said, you are writing the response in a wrong way. As an example, the following is what I did during learning about writing RESTful API serving in Golang:

type Response struct {
    StatusCode int
    Msg string
}

func respond(w http.ResponseWriter, r Response) {
    // if r.StatusCode == http.StatusUnauthorized {
    //     w.Header().Add("WWW-Authenticate", `Basic realm="Authorization Required"`)
    // }

    data, err := json.Marshal(r)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Fprintf(w, err.Error())
        return 
    }

    w.WriteHeader(r.StatusCode)
    fmt.Fprintf(w, r.Msg)
}

func Hello(w http.ResponseWriter, r *http.Request) {
    resp := Response{http.StatusOK, welcome}
    respond(w, resp)
}

Ref: https://github.com/shudipta/Book-Server/blob/master/book_server/book_server.go

Hope, this will help.

Upvotes: 1

Shashank V
Shashank V

Reputation: 11233

It should be plain text only.

From docs

func Error(w ResponseWriter, error string, code int)

Error replies to the request with the specified error message and HTTP code. It does not otherwise end the request; the caller should ensure no further writes are done to w. The error message should be plain text.

Also I think your usage of http.Error is not correct. When you call w.Write(data), the response is sent and response body will be closed. That is why you are getting 200 status instead of 500 from http.Error.

Instead of using http.Error, you can send your own error response with json just like how you would send any other response by setting the status code to an error code.

Upvotes: 5

craigmj
craigmj

Reputation: 5077

I've discovered that it's really easy to read the Go source. If you click on the function in the docs, you will be taken to the source for the Error function: https://golang.org/src/net/http/server.go?s=61907:61959#L2006

// Error replies to the request with the specified error message and HTTP code.
// It does not otherwise end the request; the caller should ensure no further
// writes are done to w.
// The error message should be plain text.
func Error(w ResponseWriter, error string, code int) {
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.WriteHeader(code)
    fmt.Fprintln(w, error)
}

So if you want to return JSON, it's easy enough to write your own Error function.

func JSONError(w http.ResponseWriter, err interface{}, code int) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.WriteHeader(code)
    json.NewEncoder(w).Encode(err)
}

Upvotes: 22

Related Questions