How can I customize Golang http 400 responses for

2019-05-07 11:43发布

问题:

I've written an REST API service that requires that all responses be JSON. However, when the Go http request parser encounters an error, it returns 400 as a plain text response without ever calling my handlers. Example:

> curl -i -H 'Authorization: Basic hi    
there' 'http://localhost:8080/test' -v
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Basic hi
> there
> 
< HTTP/1.1 400 Bad Request
HTTP/1.1 400 Bad Request
< Content-Type: text/plain; charset=utf-8
Content-Type: text/plain; charset=utf-8
< Connection: close
Connection: close

< 
* Closing connection 0

Note the invalid Authorization header. Of course 400 is the proper response, but it's text/plain, of course. Is there some way to configure the Go http parser to use custom error response media types and bodies?

回答1:

You can't. You can find this in net/http source, it only happens if the request was malformed:

https://github.com/golang/go/blob/master/src/net/http/server.go#L1744

I think your problem might be a new line in the header you're adding in curl?

401, 403, 404, 500 errors you'll be able to respond with json, but bad requests or bad headers (too long, malformed) are handled within server.go.

There is at present no way to intercept such errors though it is under consideration, so your only solution in go would be to patch the stdlib source (I don't recommend this). However, since this error only presents if the client has made a mistake and the request is malformed, it's probably not a huge problem. The reason for the text response is so that a browser or similar client (like curl without -v) doesn't just see an empty response. You could put a proxy like nginx in front of your app but then you'd never see the request either as it is a bad request, your proxy would handle it.

Possibly you'd be able to do it with a proxy like nginx in front though if you set a specific static error page for it to serve for 400 errors and serve a 400.json file that you specify? That's the only solution I can think of. A directive something like this might work for nginx:

error_page 400 /400.json;

If you'd like to be able to customise these errors, perhaps add a comment to the issue linked to let them know you had this specific problem.



回答2:

If you are using the standard net/http library you can use the following code. Take a look at this answer Showing custom 404 error page with standard http package @Mostafa to which I got this example from

func homeHandler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        errorHandler(w, r, http.StatusNotFound)
        return
    }
    fmt.Fprint(w, "welcome home")
}

func errorHandler(w http.ResponseWriter, r *http.Request, status int) {
    w.WriteHeader(status)
    if status == http.StatusNotFound {
        // JSON Out here
    }
}