diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b1e35bb35..a30c65303 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -36,12 +36,6 @@ jobs: go build ./... go test ./... - - name: Errors - run: | - cd cmd/protoc-gen-go-errors - go build ./... - go test ./... - - name: Examples run: | cd examples diff --git a/api/metadata/metadata_http.pb.go b/api/metadata/metadata_http.pb.go index 55eeb3647..d4f6f3d1a 100644 --- a/api/metadata/metadata_http.pb.go +++ b/api/metadata/metadata_http.pb.go @@ -26,64 +26,11 @@ type MetadataHandler interface { } func NewMetadataHandler(srv MetadataHandler, opts ...http1.HandleOption) http.Handler { - h := http1.DefaultHandleOptions() - for _, o := range opts { - o(&h) - } r := mux.NewRouter() - r.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { - var in ListServicesRequest - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } + r.Handle("/services", http1.NewHandler(srv.ListServices, opts...)).Methods("GET") - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.ListServices(ctx, req.(*ListServicesRequest)) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - reply := out.(*ListServicesReply) - if err := h.Encode(w, r, reply); err != nil { - h.Error(w, r, err) - } - }).Methods("GET") - - r.HandleFunc("/services/{name}", func(w http.ResponseWriter, r *http.Request) { - var in GetServiceDescRequest - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } - - if err := binding.MapProto(&in, mux.Vars(r)); err != nil { - h.Error(w, r, err) - return - } - - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.GetServiceDesc(ctx, req.(*GetServiceDescRequest)) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - reply := out.(*GetServiceDescReply) - if err := h.Encode(w, r, reply); err != nil { - h.Error(w, r, err) - } - }).Methods("GET") + r.Handle("/services/{name}", http1.NewHandler(srv.GetServiceDesc, opts...)).Methods("GET") return r } diff --git a/cmd/protoc-gen-go-errors/errors.go b/cmd/protoc-gen-go-errors/errors.go deleted file mode 100644 index 081b132eb..000000000 --- a/cmd/protoc-gen-go-errors/errors.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - pb "github.com/go-kratos/kratos/v2/api/kratos/api" - - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/proto" -) - -const ( - errorsPackage = protogen.GoImportPath("github.com/go-kratos/kratos/v2/errors") -) - -// generateFile generates a _http.pb.go file containing kratos errors definitions. -func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { - if len(file.Enums) == 0 { - return nil - } - filename := file.GeneratedFilenamePrefix + "_errors.pb.go" - g := gen.NewGeneratedFile(filename, file.GoImportPath) - g.P("// Code generated by protoc-gen-go-errors. DO NOT EDIT.") - g.P() - g.P("package ", file.GoPackageName) - g.P() - generateFileContent(gen, file, g) - return g -} - -// generateFileContent generates the kratos errors definitions, excluding the package statement. -func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) { - if len(file.Enums) == 0 { - return - } - - g.P("// This is a compile-time assertion to ensure that this generated file") - g.P("// is compatible with the kratos package it is being compiled against.") - g.P("const _ = ", errorsPackage.Ident("SupportPackageIsVersion1")) - g.P() - for _, enum := range file.Enums { - genErrorsReason(gen, file, g, enum) - } -} - -func genErrorsReason(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, enum *protogen.Enum) { - err := proto.GetExtension(enum.Desc.Options(), pb.E_Errors) - if ok := err.(bool); !ok { - return - } - var ew errorWrapper - for _, v := range enum.Values { - err := &errorInfo{ - Name: string(enum.Desc.Name()), - Value: string(v.Desc.Name()), - } - ew.Errors = append(ew.Errors, err) - } - g.P(ew.execute()) -} diff --git a/cmd/protoc-gen-go-errors/go.mod b/cmd/protoc-gen-go-errors/go.mod deleted file mode 100644 index fbc26fe00..000000000 --- a/cmd/protoc-gen-go-errors/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2 - -go 1.15 - -require ( - github.com/go-kratos/kratos/v2 v2.0.0-beta2 - google.golang.org/protobuf v1.25.0 -) diff --git a/cmd/protoc-gen-go-errors/go.sum b/cmd/protoc-gen-go-errors/go.sum deleted file mode 100644 index 447a415b3..000000000 --- a/cmd/protoc-gen-go-errors/go.sum +++ /dev/null @@ -1,95 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-kratos/kratos/v2 v2.0.0-beta2 h1:KfYlxfXsjRiccGS1TVRKvXVuEnPJDwrrTSx5HtaOBlc= -github.com/go-kratos/kratos/v2 v2.0.0-beta2/go.mod h1:hwEYWw8GFuJ8IoNt3T/3k+7kUfYt+h2fHDcyFlR1jBA= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.18.0/go.mod h1:PT5zQj4lTsR1YeARt8YNKcFb88/c2IKoSABK9mX0r78= -go.opentelemetry.io/otel/metric v0.18.0/go.mod h1:kEH2QtzAyBy3xDVQfGZKIcok4ZZFvd5xyKPfPcuK6pE= -go.opentelemetry.io/otel/oteltest v0.18.0/go.mod h1:NyierCU3/G8DLTva7KRzGii2fdxdR89zXKH1bNWY7Bo= -go.opentelemetry.io/otel/trace v0.18.0/go.mod h1:FzdUu3BPwZSZebfQ1vl5/tAa8LyMLXSJN57AXIt/iDk= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/cmd/protoc-gen-go-errors/main.go b/cmd/protoc-gen-go-errors/main.go deleted file mode 100644 index 7eab8c54e..000000000 --- a/cmd/protoc-gen-go-errors/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "flag" - "fmt" - - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/types/pluginpb" -) - -const version = "v2.0.0-beta4" - -func main() { - showVersion := flag.Bool("version", false, "print the version and exit") - flag.Parse() - if *showVersion { - fmt.Printf("protoc-gen-go-errors %v\n", version) - return - } - - var flags flag.FlagSet - - protogen.Options{ - ParamFunc: flags.Set, - }.Run(func(gen *protogen.Plugin) error { - gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) - for _, f := range gen.Files { - if !f.Generate { - continue - } - generateFile(gen, f) - } - return nil - }) -} diff --git a/cmd/protoc-gen-go-errors/template.go b/cmd/protoc-gen-go-errors/template.go deleted file mode 100644 index 1d86bb1d2..000000000 --- a/cmd/protoc-gen-go-errors/template.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "bytes" - "text/template" -) - -var errorsTemplate = `const ( -{{ range .Errors }} - Errors_{{.Value}} = "{{.Name}}_{{.Value}}" -{{- end }} -) - -{{ range .Errors }} - -func Is{{.Value}}(err error) bool { - return errors.Reason(err) == Errors_{{.Value}} -} -{{- end }} -` - -type errorInfo struct { - Name string - Value string -} -type errorWrapper struct { - Errors []*errorInfo -} - -func (e *errorWrapper) execute() string { - buf := new(bytes.Buffer) - tmpl, err := template.New("errors").Parse(errorsTemplate) - if err != nil { - panic(err) - } - if err := tmpl.Execute(buf, e); err != nil { - panic(err) - } - return string(buf.Bytes()) -} diff --git a/cmd/protoc-gen-go-http/template.go b/cmd/protoc-gen-go-http/template.go index add156dd7..d487252e2 100644 --- a/cmd/protoc-gen-go-http/template.go +++ b/cmd/protoc-gen-go-http/template.go @@ -14,40 +14,9 @@ type {{.ServiceType}}Handler interface { } func New{{.ServiceType}}Handler(srv {{.ServiceType}}Handler, opts ...http1.HandleOption) http.Handler { - h := http1.DefaultHandleOptions() - for _, o := range opts { - o(&h) - } r := mux.NewRouter() {{range .Methods}} - r.HandleFunc("{{.Path}}", func(w http.ResponseWriter, r *http.Request) { - var in {{.Request}} - if err := h.Decode(r, &in{{.Body}}); err != nil { - h.Error(w, r, err) - return - } - {{if ne (len .Vars) 0}} - if err := binding.MapProto(&in, mux.Vars(r)); err != nil { - h.Error(w, r, err) - return - } - {{end}} - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.{{.Name}}(ctx, req.(*{{.Request}})) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - reply := out.(*{{.Reply}}) - if err := h.Encode(w, r, reply{{.ResponseBody}}); err != nil { - h.Error(w, r, err) - } - }).Methods("{{.Method}}") + r.Handle("{{.Path}}", http1.NewHandler(srv.{{.Name}}, opts...)).Methods("{{.Method}}") {{end}} return r } diff --git a/examples/blog/api/blog/v1/blog_http.pb.go b/examples/blog/api/blog/v1/blog_http.pb.go index 6d51691c0..e73adf3c5 100644 --- a/examples/blog/api/blog/v1/blog_http.pb.go +++ b/examples/blog/api/blog/v1/blog_http.pb.go @@ -32,146 +32,17 @@ type BlogServiceHandler interface { } func NewBlogServiceHandler(srv BlogServiceHandler, opts ...http1.HandleOption) http.Handler { - h := http1.DefaultHandleOptions() - for _, o := range opts { - o(&h) - } r := mux.NewRouter() - r.HandleFunc("/v1/article/", func(w http.ResponseWriter, r *http.Request) { - var in CreateArticleRequest - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } - - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.CreateArticle(ctx, req.(*CreateArticleRequest)) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - reply := out.(*CreateArticleReply) - if err := h.Encode(w, r, reply); err != nil { - h.Error(w, r, err) - } - }).Methods("POST") - - r.HandleFunc("/v1/article/{id}", func(w http.ResponseWriter, r *http.Request) { - var in UpdateArticleRequest - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } - - if err := binding.MapProto(&in, mux.Vars(r)); err != nil { - h.Error(w, r, err) - return - } - - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.UpdateArticle(ctx, req.(*UpdateArticleRequest)) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - reply := out.(*UpdateArticleReply) - if err := h.Encode(w, r, reply); err != nil { - h.Error(w, r, err) - } - }).Methods("PUT") - - r.HandleFunc("/v1/article/{id}", func(w http.ResponseWriter, r *http.Request) { - var in DeleteArticleRequest - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } - - if err := binding.MapProto(&in, mux.Vars(r)); err != nil { - h.Error(w, r, err) - return - } - - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.DeleteArticle(ctx, req.(*DeleteArticleRequest)) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - reply := out.(*DeleteArticleReply) - if err := h.Encode(w, r, reply); err != nil { - h.Error(w, r, err) - } - }).Methods("DELETE") - - r.HandleFunc("/v1/article/{id}", func(w http.ResponseWriter, r *http.Request) { - var in GetArticleRequest - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } - - if err := binding.MapProto(&in, mux.Vars(r)); err != nil { - h.Error(w, r, err) - return - } - - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.GetArticle(ctx, req.(*GetArticleRequest)) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - reply := out.(*GetArticleReply) - if err := h.Encode(w, r, reply); err != nil { - h.Error(w, r, err) - } - }).Methods("GET") - - r.HandleFunc("/v1/article/", func(w http.ResponseWriter, r *http.Request) { - var in ListArticleRequest - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } - - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.ListArticle(ctx, req.(*ListArticleRequest)) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - reply := out.(*ListArticleReply) - if err := h.Encode(w, r, reply); err != nil { - h.Error(w, r, err) - } - }).Methods("GET") + r.Handle("/v1/article/", http1.NewHandler(srv.CreateArticle, opts...)).Methods("POST") + + r.Handle("/v1/article/{id}", http1.NewHandler(srv.UpdateArticle, opts...)).Methods("PUT") + + r.Handle("/v1/article/{id}", http1.NewHandler(srv.DeleteArticle, opts...)).Methods("DELETE") + + r.Handle("/v1/article/{id}", http1.NewHandler(srv.GetArticle, opts...)).Methods("GET") + + r.Handle("/v1/article/", http1.NewHandler(srv.ListArticle, opts...)).Methods("GET") return r } diff --git a/examples/helloworld/helloworld/helloworld.pb.go b/examples/helloworld/helloworld/helloworld.pb.go index 0ca50cfc2..5a158473e 100644 --- a/examples/helloworld/helloworld/helloworld.pb.go +++ b/examples/helloworld/helloworld/helloworld.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0 -// protoc v3.13.0 +// protoc-gen-go v1.26.0 +// protoc v3.14.0 // source: helloworld.proto package helloworld import ( - proto "github.com/golang/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -22,10 +21,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - // The request message containing the user's name. type HelloRequest struct { state protoimpl.MessageState diff --git a/examples/helloworld/helloworld/helloworld_http.pb.go b/examples/helloworld/helloworld/helloworld_http.pb.go index 1b154608d..8199ede20 100644 --- a/examples/helloworld/helloworld/helloworld_http.pb.go +++ b/examples/helloworld/helloworld/helloworld_http.pb.go @@ -24,39 +24,9 @@ type GreeterHandler interface { } func NewGreeterHandler(srv GreeterHandler, opts ...http1.HandleOption) http.Handler { - h := http1.DefaultHandleOptions() - for _, o := range opts { - o(&h) - } r := mux.NewRouter() - r.HandleFunc("/helloworld/{name}", func(w http.ResponseWriter, r *http.Request) { - var in HelloRequest - - if err := binding.MapProto(&in, mux.Vars(r)); err != nil { - h.Error(w, r, err) - return - } - - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.SayHello(ctx, req.(*HelloRequest)) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - if err := h.Encode(w, r, out); err != nil { - h.Error(w, r, err) - } - }).Methods("GET") + r.Handle("/helloworld/{name}", http1.NewHandler(srv.SayHello, opts...)).Methods("GET") return r } diff --git a/examples/http/handler/main.go b/examples/http/handler/main.go new file mode 100644 index 000000000..c65ae9e0c --- /dev/null +++ b/examples/http/handler/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "context" + "log" + + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/transport/http" + "github.com/gorilla/mux" +) + +// User is a user model. +type User struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// UserService is a user service. +type UserService struct{ users map[string]*User } + +// Get get a user from memory. +func (u *UserService) Get(ctx context.Context, user *User) (*User, error) { + return u.users[user.ID], nil +} + +// Add add a user to memory. +func (u *UserService) Add(ctx context.Context, user *User) (*User, error) { + u.users[user.ID] = user + return user, nil +} + +func main() { + us := &UserService{ + users: make(map[string]*User), + } + router := mux.NewRouter() + router.Handle("/users", http.NewHandler(us.Add)).Methods("POST") + router.Handle("/users/{id}", http.NewHandler(us.Get)).Methods("GET") + + httpSrv := http.NewServer(http.Address(":8000")) + httpSrv.HandlePrefix("/", router) + + app := kratos.New( + kratos.Name("handler"), + kratos.Server( + httpSrv, + ), + ) + if err := app.Run(); err != nil { + log.Println(err) + } +} diff --git a/transport/http/binding/bind.go b/transport/http/binding/bind.go index 7549c3637..580722ac5 100644 --- a/transport/http/binding/bind.go +++ b/transport/http/binding/bind.go @@ -16,3 +16,15 @@ func BindForm(req *http.Request, target interface{}) error { } return mapForm(target, req.Form) } + +// BindValue bind map parameters to target. +func BindValue(vars map[string]string, target interface{}) error { + values := make(map[string][]string, len(vars)) + for k, v := range vars { + values[k] = []string{v} + } + if msg, ok := target.(proto.Message); ok { + return mapProto(msg, values) + } + return mapForm(target, values) +} diff --git a/transport/http/binding/proto.go b/transport/http/binding/proto.go index 60bc3817e..a56d693c5 100644 --- a/transport/http/binding/proto.go +++ b/transport/http/binding/proto.go @@ -18,6 +18,7 @@ import ( ) // MapProto sets a value in a nested Protobuf structure. +// Deprecated: use BindValue instead. func MapProto(msg proto.Message, values map[string]string) error { for key, value := range values { if err := populateFieldValues(msg.ProtoReflect(), strings.Split(key, "."), []string{value}); err != nil { diff --git a/transport/http/handle.go b/transport/http/handle.go index 16c69b89e..271ca4bf7 100644 --- a/transport/http/handle.go +++ b/transport/http/handle.go @@ -1,14 +1,20 @@ package http import ( + "context" + "fmt" "io/ioutil" "net/http" + "reflect" "github.com/go-kratos/kratos/v2/encoding" "github.com/go-kratos/kratos/v2/encoding/json" xhttp "github.com/go-kratos/kratos/v2/internal/http" "github.com/go-kratos/kratos/v2/middleware" + "github.com/go-kratos/kratos/v2/middleware/recovery" "github.com/go-kratos/kratos/v2/transport/http/binding" + "github.com/gorilla/mux" + "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/protojson" ) @@ -26,64 +32,134 @@ type EncodeResponseFunc func(http.ResponseWriter, *http.Request, interface{}) er type EncodeErrorFunc func(http.ResponseWriter, *http.Request, error) // HandleOption is handle option. -type HandleOption func(*HandleOptions) - -// HandleOptions is handle options. -type HandleOptions struct { - Decode DecodeRequestFunc - Encode EncodeResponseFunc - Error EncodeErrorFunc - Middleware middleware.Middleware -} - -// DefaultHandleOptions returns a default handle options. -func DefaultHandleOptions() HandleOptions { - return HandleOptions{ - Decode: decodeRequest, - Encode: encodeResponse, - Error: encodeError, - } -} +type HandleOption func(*Handler) // RequestDecoder with request decoder. func RequestDecoder(dec DecodeRequestFunc) HandleOption { - return func(o *HandleOptions) { - o.Decode = dec + return func(o *Handler) { + o.dec = dec } } // ResponseEncoder with response encoder. func ResponseEncoder(en EncodeResponseFunc) HandleOption { - return func(o *HandleOptions) { - o.Encode = en + return func(o *Handler) { + o.enc = en } } // ErrorEncoder with error encoder. func ErrorEncoder(en EncodeErrorFunc) HandleOption { - return func(o *HandleOptions) { - o.Error = en + return func(o *Handler) { + o.err = en } } // Middleware with middleware option. func Middleware(m ...middleware.Middleware) HandleOption { - return func(o *HandleOptions) { - o.Middleware = middleware.Chain(m...) + return func(o *Handler) { + o.next = middleware.Chain(m...) } } +// Handler is handle options. +type Handler struct { + method reflect.Value + in reflect.Type + out reflect.Type + dec DecodeRequestFunc + enc EncodeResponseFunc + err EncodeErrorFunc + next middleware.Middleware +} + +// NewHandler new a HTTP handler. +func NewHandler(handler interface{}, opts ...HandleOption) http.Handler { + if err := validateHandler(handler); err != nil { + panic(err) + } + typ := reflect.TypeOf(handler) + h := &Handler{ + method: reflect.ValueOf(handler), + in: typ.In(1).Elem(), + out: typ.Out(0).Elem(), + dec: decodeRequest, + enc: encodeResponse, + err: encodeError, + next: recovery.Recovery(), + } + for _, o := range opts { + o(h) + } + return h +} + +func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + in := reflect.New(h.in).Interface() + if err := h.dec(req, in); err != nil { + h.err(w, req, err) + return + } + invoke := func(ctx context.Context, in interface{}) (interface{}, error) { + ret := h.method.Call([]reflect.Value{ + reflect.ValueOf(ctx), + reflect.ValueOf(in), + }) + if ret[1].IsNil() { + return ret[0].Interface(), nil + } + return nil, ret[1].Interface().(error) + } + if h.next != nil { + invoke = h.next(invoke) + } + out, err := invoke(req.Context(), in) + if err != nil { + h.err(w, req, err) + return + } + if err := h.enc(w, req, out); err != nil { + h.err(w, req, err) + } +} + +func validateHandler(handler interface{}) error { + typ := reflect.TypeOf(handler) + if typ.NumIn() != 2 || typ.NumOut() != 2 { + return fmt.Errorf("invalid types, in: %d out: %d", typ.NumIn(), typ.NumOut()) + } + if typ.In(1).Kind() != reflect.Ptr || typ.Out(0).Kind() != reflect.Ptr { + return fmt.Errorf("invalid types is not a pointer") + } + if !typ.In(0).Implements(reflect.TypeOf((*context.Context)(nil)).Elem()) { + return fmt.Errorf("input does not implement the context") + } + if !typ.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { + return fmt.Errorf("input does not implement the error") + } + return nil +} + // decodeRequest decodes the request body to object. func decodeRequest(req *http.Request, v interface{}) error { subtype := xhttp.ContentSubtype(req.Header.Get(xhttp.HeaderContentType)) if codec := encoding.GetCodec(subtype); codec != nil { data, err := ioutil.ReadAll(req.Body) if err != nil { - return err + return status.Error(codes.InvalidArgument, err.Error()) + } + if err := codec.Unmarshal(data, v); err != nil { + return status.Error(codes.InvalidArgument, err.Error()) + } + } else { + if err := binding.BindForm(req, v); err != nil { + return status.Error(codes.InvalidArgument, err.Error()) } - return codec.Unmarshal(data, v) } - return binding.BindForm(req, v) + if err := binding.BindValue(mux.Vars(req), v); err != nil { + return status.Error(codes.InvalidArgument, err.Error()) + } + return nil } // encodeResponse encodes the object to the HTTP response. diff --git a/transport/http/handle_test.go b/transport/http/handle_test.go index e5049ffee..0165c97d7 100644 --- a/transport/http/handle_test.go +++ b/transport/http/handle_test.go @@ -2,10 +2,7 @@ package http import ( "context" - "net/http" "testing" - - "github.com/gorilla/mux" ) type HelloRequest struct { @@ -21,37 +18,7 @@ func (s *GreeterService) SayHello(ctx context.Context, req *HelloRequest) (*Hell return &HelloReply{Message: "hello " + req.Name}, nil } -func newGreeterHandler(srv *GreeterService, opts ...HandleOption) http.Handler { - h := DefaultHandleOptions() - for _, o := range opts { - o(&h) - } - r := mux.NewRouter() - r.HandleFunc("/helloworld", func(w http.ResponseWriter, r *http.Request) { - var in HelloRequest - if err := h.Decode(r, &in); err != nil { - h.Error(w, r, err) - return - } - next := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.SayHello(ctx, &in) - } - if h.Middleware != nil { - next = h.Middleware(next) - } - out, err := next(r.Context(), &in) - if err != nil { - h.Error(w, r, err) - return - } - if err := h.Encode(w, r, out); err != nil { - h.Error(w, r, err) - } - }).Methods("POST") - return r -} - func TestHandler(t *testing.T) { s := &GreeterService{} - _ = newGreeterHandler(s) + _ = NewHandler(s.SayHello) }