-
Hello everyone, so far loving the project and the tolling around. I have a simple connect service annotated with Google HTTP rules and use Vanguard for transcoding REST API calls. I can see that Vanguard automatically takes care of returning the appropriate HTTP status code depending on the connect status code attached to the returned error from the Handler but I couldn't find a way to change the response body. Currently, the response body returned to HTTP clients in case of an error looks like this: {
"code": "<the connect code>",
"message":"<error message>",
"details":[] // any details attached
} I would like in case of error to return a more "standard" structure expected by REST APIs. I did find in the documentation the ErrorWriter which seems very close to what I want but if I understand correctly it's only useful for writing HTTP middleware that works with RPC clients and I can't think of a way to solve my use case. Any Ideas? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
The This is nearly identical to what vanguard-go already does. The subtle difference is that IIRC vanguard-go serializes the code as a number instead of a string and the detail messages, when present, are a little bit different. But it's still a JSON response with the same information. The reason vanguard uses the format it does was meant for compatibility with grpc-gateway and Envoy's gRPC-JSON transcoding filter, which both also implement HTTP transcoding. Vanguard intentionally doesn't provide a way to customize the error body because it could pose a potential interop issue with these other implementations, or client implementations built to expect the error format that these other solutions also emit. You could technically re-write the body via HTTP middleware that wraps the Vanguard transcoder. This middleware could buffer the body when the response is not 200 and then decode it, transform it, and then serialize the new form to the underlying |
Beta Was this translation helpful? Give feedback.
-
For anyone stumbling into this, with @jhump help I wrote this HTTP middleware to return custom error responses depending on the error details attached: Do note that this shouldn't be used for streaming RPCs. import (
"bytes"
"net/http"
"connectrpc.com/connect"
"google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/protobuf/encoding/protojson"
)
type WrappedResponseWriter struct {
Body bytes.Buffer
StatusCode int
rw http.ResponseWriter
}
func (w *WrappedResponseWriter) Header() http.Header {
return w.rw.Header()
}
func (w *WrappedResponseWriter) Write(buf []byte) (int, error) {
return w.Body.Write(buf)
}
func (w *WrappedResponseWriter) WriteHeader(statusCode int) {
w.StatusCode = statusCode
w.rw.WriteHeader(statusCode)
}
func (w *WrappedResponseWriter) Flush() {}
func HTTPTranscoderErrorWrapper(next http.Handler) http.Handler {
errWriter := connect.NewErrorWriter()
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if errWriter.IsSupported(r) {
next.ServeHTTP(w, r)
}
wrappedRW := &WrappedResponseWriter{rw: w}
next.ServeHTTP(wrappedRW, r)
if wrappedRW.StatusCode == http.StatusOK {
_, err := w.Write(wrappedRW.Body.Bytes())
if err != nil {
// handle error
}
return
}
st := &status.Status{}
err := protojson.Unmarshal(wrappedRW.Body.Bytes(), st)
if err != nil {
// handle error
}
for _, detail := range st.Details {
m, err := detail.UnmarshalNew()
if err != nil {
// handle error
}
switch t := m.(type) {
case *errdetails.ErrorInfo:
// return custom payload
}
}
}
} |
Beta Was this translation helpful? Give feedback.
The
connect.ErrorWriter
would use the Connect error format:https://connectrpc.com/docs/protocol#error-end-stream
This is nearly identical to what vanguard-go already does. The subtle difference is that IIRC vanguard-go serializes the code as a number instead of a string and the detail messages, when present, are a little bit different. But it's still a JSON response with the same information.
The reason vanguard uses the format it does was meant for compatibility with grpc-gateway and Envoy's gRPC-JSON transcoding filter, which both also implement HTTP transcoding. Vanguard intentionally doesn't provide a way to customize the error body because it could pose a potential interop issue wit…