Golang. Adding a json body to POST request in protofile

It could be a bit tricky, and I failed to find a good example for it.

my-api.proto

service MyAPI {
    rpc UpdateItem (Update_Request) returns (Update_Response) {
        option (google.api.http) = {
            post: "/api/v5/item/{code}"
            body: "request_body"
        };
    }
}
...

message Update_Request {
    string code = 1 [json_name = "code"];
    int32 language_id = 2  [json_name = "language_id"];
    Update_RequestBody request_body = 3  [json_name = "request_body"];
}

message Update_RequestBody {
    optional Address address = 1 [json_name = "address"];
    optional string update_time = 2 [json_name = "update_time", (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {example: "\"2019-02-02T14:00:00+0100\""}];
}

message Address {
    optional double latitude = 1 [json_name = "latitude"];
    optional double longitude = 2 [json_name = "longitude"];
    optional string formatted_customer_address = 3 [json_name = "formatted_customer_address", (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {example: "\"Berlin Stallschreiberstrasse 10\""}];
    optional string title = 4 [json_name = "title", (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {example: "\"Home\""}];
}

Optionally, you can add the following middleware to allow an empty request body from the client as any JSON is required by grpc-ecosystem.

// POSTRequestBodyFixer is a middleware that fixes empty POST request body
// as it is always expected to be a valid JSON by grpc-ecosystem.
func POSTRequestBodyFixer(defaultBody string) httpmw.Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Method == http.MethodPost && r.Body != nil {
				body, err := ioutil.ReadAll(r.Body)
				if err != nil {
					errorhandler.HandleErrorResponse(err, w)
					return
				}
				// close the body since we override it with ioutil.NopCloser
				_ = r.Body.Close()

				responseBody := string(body)
				if responseBody == "" {
					responseBody = defaultBody
				}
				r.Body = io.NopCloser(strings.NewReader(responseBody))
			}

			next.ServeHTTP(w, r)
		})
	}
}

LEAVE A COMMENT