protoc-gen-go-http
This protobuf code generator generates Go HTTP bindings for RPC services defined in .proto
files.
It uses Google API HTTP protobuf extension to resolve HTTP parameters (path, method etc) for each method and creates Go http.Handler
to apply routing, marshalling and unmarshalling.
Only few basic features of Google API HTTP annotations are supported at the moment:
- All HTTP methods including custom
- Simple URL parameters for example
get=/users/{user_id}/comments/{comment_id}
- Simple request body parsing (no body,
body='*'
and body='argument_name'
)
- Basic query parameters of type: string, int, float
- GRPC to HTTP status code mapping
- Additional bindings (additional_binding annotation to define more than one HTTP binding)
This code generator does not intend to generate perfect RESTful API (use all of the Status Codes, Headers, Query parameters, Content Type Negotiation etc).
The idea of this code generator is to provide "good enough" HTTP API on backed by the very same server implementation used by GRPC server, so you can have one
server implementation and multiple network bindings.
Installation
go install github.com/eolymp/go-proto-http/cmd/protoc-gen-go-http
Usage
Just like with any other code generator you need to run protoc
with go-http_out
flag. This code generator requires data structures and interfaces generated by go
and go-grpc
code generators, so they should be enabled too.
protoc --go_out=build --go-grpc_out=build --go-http_out=build your.proto
You can find full example in /example
folder
Let's say we have proto file which defines following UserManager
service:
service UserManager {
rpc CreateUser (CreateUserInput) returns (CreateUserOutput) {
option (google.api.http) = { post: "/users" body: "*" };
}
rpc DeleteUser (DeleteUserInput) returns (DeleteUserOutput) {
option (google.api.http) = { delete: "/users/{user_id}" };
}
rpc GetComments (GetCommentsInput) returns (GetCommentsOutput) {
option (google.api.http) = { get: "/users/{user_id}/comments" };
}
rpc CreateComment (CreateCommentInput) returns (CreateCommentOutput) {
option (google.api.http) = { post: "/users/{user_id}/comments" body: "comment" };
}
}
From annotations in this file protoc-gen-go-http
will generate an http.Handler
which would handler routing (using mux package) and converting http.Request
into corresponding protobuf data structures.
Generated code looks something like this:
// ...
// NewUserManagerHandler constructs new http.Handler for UserManagerServer
func NewUserManagerHandler(srv UserManagerServer) http.Handler {
router := mux.NewRouter()
router.Handle("/users", _UserManager_CreateUser_HTTP_Handler(srv)).Methods("POST")
router.Handle("/users/{user_id}", _UserManager_DeleteUser_HTTP_Handler(srv)).Methods("DELETE")
router.Handle("/users/{user_id}/comments", _UserManager_GetComments_HTTP_Handler(srv)).Methods("GET")
router.Handle("/users/{user_id}/comments", _UserManager_CreateComment_HTTP_Handler(srv)).Methods("POST")
return router
}
// ...
func _UserManager_CreateComment_HTTP_Handler(srv UserManagerServer) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
in := &CreateCommentInput{}
in.Comment = &Comment{}
if err := _UserManager_HTTPReadRequestBody(r, in.Comment); err != nil {
_UserManager_HTTPWriteErrorResponse(w, err)
return
}
vars := mux.Vars(r)
in.UserId = vars["user_id"]
out, err := srv.CreateComment(r.Context(), in)
if err != nil {
_UserManager_HTTPWriteErrorResponse(w, err)
return
}
_UserManager_HTTPWriteResponse(w, out)
})
}
Then, generated HTTP handler can be used in your application with HTTP server:
router := mux.NewRouter()
router.PathPrefix("/users").
Handler(userspb.NewUserManagerHandler(users.NewServer(db.DB)))
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal("HTTP api has failed:", err)
}