Skip to content

Commit

Permalink
docs: add example to fully override http responses (#4564)
Browse files Browse the repository at this point in the history
* docs: add example to fully override http responses

* docs: chore comment

* docs: resolve feedback

* docs(ADOPTERS): add Cho Tot
  • Loading branch information
nguyentranbao-ct authored Jul 30, 2024
1 parent 1982632 commit 64d02b0
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 1 deletion.
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

0 comments on commit 64d02b0

Please sign in to comment.