From 71e188ee211d2f6065ffb9b291eb8e7663279936 Mon Sep 17 00:00:00 2001 From: Zhong Wang Date: Tue, 29 Nov 2016 13:28:46 -0800 Subject: [PATCH] Make grpc-gateway support enum fields in path parameter; repeated enum fields are also supported. --- README.md | 45 +++++++++++++++++++++++---------------------- runtime/query.go | 8 ++++---- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index f5629b1f341..8ae7c244349 100644 --- a/README.md +++ b/README.md @@ -43,12 +43,12 @@ go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger go get -u github.com/golang/protobuf/protoc-gen-go ``` - + ## Usage Make sure that your `$GOPATH/bin` is in your `$PATH`. 1. Define your service in gRPC - + your_service.proto: ```protobuf syntax = "proto3"; @@ -56,13 +56,13 @@ Make sure that your `$GOPATH/bin` is in your `$PATH`. message StringMessage { string value = 1; } - + service YourService { rpc Echo(StringMessage) returns (StringMessage) {} } ``` 2. Add a [custom option](https://cloud.google.com/service-management/reference/rpc/google.api#http) to the .proto file - + your_service.proto: ```diff syntax = "proto3"; @@ -73,7 +73,7 @@ Make sure that your `$GOPATH/bin` is in your `$PATH`. message StringMessage { string value = 1; } - + service YourService { - rpc Echo(StringMessage) returns (StringMessage) {} + rpc Echo(StringMessage) returns (StringMessage) { @@ -85,7 +85,7 @@ Make sure that your `$GOPATH/bin` is in your `$PATH`. } ``` 3. Generate gRPC stub - + ```sh protoc -I/usr/local/include -I. \ -I$GOPATH/src \ @@ -93,11 +93,11 @@ Make sure that your `$GOPATH/bin` is in your `$PATH`. --go_out=Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,plugins=grpc:. \ path/to/your_service.proto ``` - + It will generate a stub file `path/to/your_service.pb.go`. 4. Implement your service in gRPC as usual 1. (Optional) Generate gRPC stub in the language you want. - + e.g. ```sh protoc -I/usr/local/include -I. \ @@ -105,7 +105,7 @@ Make sure that your `$GOPATH/bin` is in your `$PATH`. -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ --ruby_out=. \ path/to/your/service_proto - + protoc -I/usr/local/include -I. \ -I$GOPATH/src \ -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ @@ -115,7 +115,7 @@ Make sure that your `$GOPATH/bin` is in your `$PATH`. ``` 2. Implement your service 5. Generate reverse-proxy - + ```sh protoc -I/usr/local/include -I. \ -I$GOPATH/src \ @@ -123,49 +123,49 @@ Make sure that your `$GOPATH/bin` is in your `$PATH`. --grpc-gateway_out=logtostderr=true:. \ path/to/your_service.proto ``` - + It will generate a reverse proxy `path/to/your_service.pb.gw.go`. 6. Write an entrypoint - + Now you need to write an entrypoint of the proxy server. ```go package main import ( "flag" "net/http" - + "github.com/golang/glog" "golang.org/x/net/context" "github.com/grpc-ecosystem/grpc-gateway/runtime" "google.golang.org/grpc" - + gw "path/to/your_service_package" ) - + var ( echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of YourService") ) - + func run() error { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() - + mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithInsecure()} err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts) if err != nil { return err } - + http.ListenAndServe(":8080", mux) return nil } - + func main() { flag.Parse() defer glog.Flush() - + if err := run(); err != nil { glog.Fatal(err) } @@ -204,6 +204,7 @@ To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), gRP * Method parameters in request body * Method parameters in request path * Method parameters in query string +* Enum fields in path parameter (including repeated enum fields). * Mapping streaming APIs to newline-delimited JSON streams * Mapping HTTP headers with `Grpc-Metadata-` prefix to gRPC metadata * Optionally emitting API definition for [Swagger](http://swagger.io). @@ -211,7 +212,7 @@ To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), gRP ### Want to support But not yet. -* bytes and enum fields in path parameter. #5 +* bytes fields in path parameter. #5 * Optionally generating the entrypoint. #8 * `import_path` parameter @@ -227,7 +228,7 @@ But patch is welcome. * [How gRPC error codes map to HTTP status codes in the response](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/errors.go#L15) * HTTP request source IP is added as `X-Forwarded-For` gRPC request header * HTTP request host is added as `X-Forwarded-Host` gRPC request header -* HTTP `Authorization` header is added as `authorization` gRPC request header +* HTTP `Authorization` header is added as `authorization` gRPC request header * Remaining HTTP header keys are prefixed with `Grpc-Metadata-` and added with their values to gRPC request header * While configurable, the default {un,}marshaling uses [jsonpb](https://godoc.org/github.com/golang/protobuf/jsonpb) with `OrigName: true`. diff --git a/runtime/query.go b/runtime/query.go index 56a919a52f1..4df7717f2ac 100644 --- a/runtime/query.go +++ b/runtime/query.go @@ -62,7 +62,7 @@ func populateFieldValueFromPath(msg proto.Message, fieldPath []string, values [] case reflect.Ptr: if f.IsNil() { m = reflect.New(f.Type().Elem()) - f.Set(m) + f.Set(m.Convert(f.Type())) } m = f.Elem() continue @@ -101,13 +101,13 @@ func populateRepeatedField(f reflect.Value, values []string) error { if !ok { return fmt.Errorf("unsupported field type %s", elemType) } - f.Set(reflect.MakeSlice(f.Type(), len(values), len(values))) + f.Set(reflect.MakeSlice(f.Type(), len(values), len(values)).Convert(f.Type())) for i, v := range values { result := conv.Call([]reflect.Value{reflect.ValueOf(v)}) if err := result[1].Interface(); err != nil { return err.(error) } - f.Index(i).Set(result[0]) + f.Index(i).Set(result[0].Convert(f.Index(i).Type())) } return nil } @@ -121,7 +121,7 @@ func populateField(f reflect.Value, value string) error { if err := result[1].Interface(); err != nil { return err.(error) } - f.Set(result[0]) + f.Set(result[0].Convert(f.Type())) return nil }