Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add example to fully override http responses #4564

Merged
merged 4 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ADOPTERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ production users that have added themselves (in alphabetical order):
API of [Chef Automate](https://automate.chef.io/). Furthermore, the generated
OpenAPI data serves as the basis for its [API documentation](https://automate.chef.io/docs/api/).
The code is Open Source, [see `github.com/chef/automate`](https://github.com/chef/automate).
- [Cho Tot](https://careers.chotot.com/about-us/) utilizes gRPC Gateway to seamlessly integrate HTTP and gRPC services, enabling efficient communication for both legacy and modern systems.
- [Conduit](https://github.com/ConduitIO/conduit), a data streaming tool written in Go,
uses the gRPC-Gateway since its very beginning to provide an HTTP API in addition to its gRPC API.
This makes it easier to integrate with Conduit, and the generated OpenAPI data is used in the documentation.
Expand Down
82 changes: 81 additions & 1 deletion docs/docs/mapping/customizing_your_gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,85 @@ service Greeter {
}
```

### Fully Overriding Custom HTTP Responses

To fully override custom HTTP responses, you can use both a Forward Response Option and a Custom Marshaler.

For example with proto response message as:

```proto
message CreateUserResponse {
string name = 1;
}
```

The default HTTP response:

```json5
HTTP 200 OK
Content-Type: application/json

{"name":"John Doe"}
```

But you want to return a `201 Created` status code along with a custom response structure:

```json5
HTTP 201 Created
Content-Type: application/json

{"success":true,"data":{"name":"John Doe"}}
```

First, set up the gRPC-Gateway with the custom options:

```go
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, &ResponseWrapper{}),
runtime.WithForwardResponseOption(forwardResponse),
)
```

Define the `forwardResponse` function to handle specific response types:

```go
func forwardResponse(ctx context.Context, w http.ResponseWriter, m protoreflect.ProtoMessage) error {
switch v := m.(type) {
case *pb.CreateUserResponse:
w.WriteHeader(http.StatusCreated)
}
// keep default behavior
return nil
}
```

Create a custom marshaler to format the response data which utilizes the `JSONPb` marshaler as a fallback:

```go
type ResponseWrapper struct {
runtime.JSONPb
}

func (c *ResponseWrapper) Marshal(data any) ([]byte, error) {
resp := data
switch v := data.(type) {
case *pb.CreateUserResponse:
// wrap the response in a custom structure
resp = map[string]any{
"success": true,
"data": data,
}
}
// otherwise, use the default JSON marshaller
return c.JSONPb.Marshal(resp)
}
```

In this setup:

- The `forwardResponse` function intercepts the response and formats it as needed.
- The `CustomPB` marshaller ensures that specific types of responses are wrapped in a custom structure before being sent to the client.

## Error handler

To override error handling for a `*runtime.ServeMux`, use the
Expand Down Expand Up @@ -385,7 +464,7 @@ This method is not used outside of the initial routing.

### Customizing Routing Errors

If you want to retain HTTP `405 Method Not Allowed` instead of allowing it to be converted to the equivalent of the gRPC `12 UNIMPLEMENTED`, which is HTTP `501 Not Implmented` you can use the following example:
If you want to retain HTTP `405 Method Not Allowed` instead of allowing it to be converted to the equivalent of the gRPC `12 UNIMPLEMENTED`, which is HTTP `501 Not Implmented` you can use the following example:

```go
func handleRoutingError(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) {
Expand All @@ -405,6 +484,7 @@ func handleRoutingError(ctx context.Context, mux *runtime.ServeMux, marshaler ru
```

To use this routing error handler, construct the mux as follows:

```go
mux := runtime.NewServeMux(
runtime.WithRoutingErrorHandler(handleRoutingError),
Expand Down
Loading