Repository: go-kratos/kratos
Pull request #1076 (2021-06-17 07:11:17)

add path pattern for http


Patch:
diff --git a/api/metadata/metadata_http.pb.go b/api/metadata/metadata_http.pb.go
index 8485c35b..f30262c4 100644
--- a/api/metadata/metadata_http.pb.go
+++ b/api/metadata/metadata_http.pb.go
@@ -1,6 +1,6 @@
 // Code generated by protoc-gen-go-http. DO NOT EDIT.
 // versions:
-// protoc-gen-go-http v2.0.0-rc3
+// protoc-gen-go-http v2.0.0-rc5
 
 package metadata
 
@@ -84,8 +84,10 @@ func NewMetadataHTTPClient(client *http.Client) MetadataHTTPClient {
 
 func (c *MetadataHTTPClientImpl) GetServiceDesc(ctx context.Context, in *GetServiceDescRequest, opts ...http.CallOption) (*GetServiceDescReply, error) {
 	var out GetServiceDescReply
-	path := binding.EncodeURL("/services/{name}", in, true)
+	pattern := "/services/{name}"
+	path := binding.EncodeURL(pattern, in, true)
 	opts = append(opts, http.Operation("/kratos.api.Metadata/GetServiceDesc"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -95,8 +97,10 @@ func (c *MetadataHTTPClientImpl) GetServiceDesc(ctx context.Context, in *GetServ
 
 func (c *MetadataHTTPClientImpl) ListServices(ctx context.Context, in *ListServicesRequest, opts ...http.CallOption) (*ListServicesReply, error) {
 	var out ListServicesReply
-	path := binding.EncodeURL("/services", in, true)
+	pattern := "/services"
+	path := binding.EncodeURL(pattern, in, true)
 	opts = append(opts, http.Operation("/kratos.api.Metadata/ListServices"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
 	if err != nil {
 		return nil, err
diff --git a/cmd/kratos/main.go b/cmd/kratos/main.go
index c3c1c76b..872fe6ac 100644
--- a/cmd/kratos/main.go
+++ b/cmd/kratos/main.go
@@ -11,7 +11,7 @@ import (
 )
 
 var (
-	version string = "v2.0.0-rc3"
+	version string = "v2.0.0-rc4"
 
 	rootCmd = &cobra.Command{
 		Use:     "kratos",
diff --git a/cmd/protoc-gen-go-http/main.go b/cmd/protoc-gen-go-http/main.go
index e8f7455b..36fb4dc5 100644
--- a/cmd/protoc-gen-go-http/main.go
+++ b/cmd/protoc-gen-go-http/main.go
@@ -8,7 +8,7 @@ import (
 	"google.golang.org/protobuf/types/pluginpb"
 )
 
-const version = "v2.0.0-rc3"
+const version = "v2.0.0-rc5"
 
 func main() {
 	showVersion := flag.Bool("version", false, "print the version and exit")
diff --git a/cmd/protoc-gen-go-http/template.go b/cmd/protoc-gen-go-http/template.go
index 4a313099..7bfd85f6 100644
--- a/cmd/protoc-gen-go-http/template.go
+++ b/cmd/protoc-gen-go-http/template.go
@@ -65,8 +65,10 @@ func New{{.ServiceType}}HTTPClient (client *http.Client) {{.ServiceType}}HTTPCli
 {{range .MethodSets}}
 func (c *{{$svrType}}HTTPClientImpl) {{.Name}}(ctx context.Context, in *{{.Request}}, opts ...http.CallOption) (*{{.Reply}}, error) {
 	var out {{.Reply}}
-	path := binding.EncodeURL("{{.Path}}", in, {{.IsQuery}})
+	pattern := "{{.Path}}"
+	path := binding.EncodeURL(pattern, in, {{.IsQuery}})
 	opts = append(opts, http.Operation("/{{$svrName}}/{{.Name}}"))
+	opts = append(opts, http.PathTemplate(pattern))
 	{{if .HasBody -}}
 	err := c.cc.Invoke(ctx, "{{.Method}}", path, in{{.Body}}, &out{{.ResponseBody}}, opts...)
 	{{else -}} 
diff --git a/examples/blog/api/blog/v1/blog_http.pb.go b/examples/blog/api/blog/v1/blog_http.pb.go
index 3fcd414d..bea209a5 100644
--- a/examples/blog/api/blog/v1/blog_http.pb.go
+++ b/examples/blog/api/blog/v1/blog_http.pb.go
@@ -1,6 +1,6 @@
 // Code generated by protoc-gen-go-http. DO NOT EDIT.
 // versions:
-// protoc-gen-go-http v2.0.0-rc3
+// protoc-gen-go-http v2.0.0-rc5
 
 package v1
 
@@ -156,8 +156,10 @@ func NewBlogServiceHTTPClient(client *http.Client) BlogServiceHTTPClient {
 
 func (c *BlogServiceHTTPClientImpl) CreateArticle(ctx context.Context, in *CreateArticleRequest, opts ...http.CallOption) (*CreateArticleReply, error) {
 	var out CreateArticleReply
-	path := binding.EncodeURL("/v1/article/", in, false)
+	pattern := "/v1/article/"
+	path := binding.EncodeURL(pattern, in, false)
 	opts = append(opts, http.Operation("/blog.api.v1.BlogService/CreateArticle"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -167,8 +169,10 @@ func (c *BlogServiceHTTPClientImpl) CreateArticle(ctx context.Context, in *Creat
 
 func (c *BlogServiceHTTPClientImpl) DeleteArticle(ctx context.Context, in *DeleteArticleRequest, opts ...http.CallOption) (*DeleteArticleReply, error) {
 	var out DeleteArticleReply
-	path := binding.EncodeURL("/v1/article/{id}", in, false)
+	pattern := "/v1/article/{id}"
+	path := binding.EncodeURL(pattern, in, false)
 	opts = append(opts, http.Operation("/blog.api.v1.BlogService/DeleteArticle"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "DELETE", path, nil, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -178,8 +182,10 @@ func (c *BlogServiceHTTPClientImpl) DeleteArticle(ctx context.Context, in *Delet
 
 func (c *BlogServiceHTTPClientImpl) GetArticle(ctx context.Context, in *GetArticleRequest, opts ...http.CallOption) (*GetArticleReply, error) {
 	var out GetArticleReply
-	path := binding.EncodeURL("/v1/article/{id}", in, true)
+	pattern := "/v1/article/{id}"
+	path := binding.EncodeURL(pattern, in, true)
 	opts = append(opts, http.Operation("/blog.api.v1.BlogService/GetArticle"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -189,8 +195,10 @@ func (c *BlogServiceHTTPClientImpl) GetArticle(ctx context.Context, in *GetArtic
 
 func (c *BlogServiceHTTPClientImpl) ListArticle(ctx context.Context, in *ListArticleRequest, opts ...http.CallOption) (*ListArticleReply, error) {
 	var out ListArticleReply
-	path := binding.EncodeURL("/v1/article/", in, true)
+	pattern := "/v1/article/"
+	path := binding.EncodeURL(pattern, in, true)
 	opts = append(opts, http.Operation("/blog.api.v1.BlogService/ListArticle"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -200,8 +208,10 @@ func (c *BlogServiceHTTPClientImpl) ListArticle(ctx context.Context, in *ListArt
 
 func (c *BlogServiceHTTPClientImpl) UpdateArticle(ctx context.Context, in *UpdateArticleRequest, opts ...http.CallOption) (*UpdateArticleReply, error) {
 	var out UpdateArticleReply
-	path := binding.EncodeURL("/v1/article/{id}", in, false)
+	pattern := "/v1/article/{id}"
+	path := binding.EncodeURL(pattern, in, false)
 	opts = append(opts, http.Operation("/blog.api.v1.BlogService/UpdateArticle"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "PUT", path, in, &out, opts...)
 	if err != nil {
 		return nil, err
diff --git a/examples/helloworld/helloworld/helloworld_http.pb.go b/examples/helloworld/helloworld/helloworld_http.pb.go
index 55517338..81f78683 100644
--- a/examples/helloworld/helloworld/helloworld_http.pb.go
+++ b/examples/helloworld/helloworld/helloworld_http.pb.go
@@ -1,6 +1,6 @@
 // Code generated by protoc-gen-go-http. DO NOT EDIT.
 // versions:
-// protoc-gen-go-http v2.0.0-rc3
+// protoc-gen-go-http v2.0.0-rc5
 
 package helloworld
 
@@ -62,8 +62,10 @@ func NewGreeterHTTPClient(client *http.Client) GreeterHTTPClient {
 
 func (c *GreeterHTTPClientImpl) SayHello(ctx context.Context, in *HelloRequest, opts ...http.CallOption) (*HelloReply, error) {
 	var out HelloReply
-	path := binding.EncodeURL("/helloworld/{name}", in, true)
+	pattern := "/helloworld/{name}"
+	path := binding.EncodeURL(pattern, in, true)
 	opts = append(opts, http.Operation("/helloworld.Greeter/SayHello"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
 	if err != nil {
 		return nil, err
diff --git a/examples/traces/api/message/message_http.pb.go b/examples/traces/api/message/message_http.pb.go
index bfa19083..60a4e410 100644
--- a/examples/traces/api/message/message_http.pb.go
+++ b/examples/traces/api/message/message_http.pb.go
@@ -1,6 +1,6 @@
 // Code generated by protoc-gen-go-http. DO NOT EDIT.
 // versions:
-// protoc-gen-go-http v2.0.0-rc3
+// protoc-gen-go-http v2.0.0-rc5
 
 package v1
 
@@ -62,8 +62,10 @@ func NewMessageServiceHTTPClient(client *http.Client) MessageServiceHTTPClient {
 
 func (c *MessageServiceHTTPClientImpl) GetUserMessage(ctx context.Context, in *GetUserMessageRequest, opts ...http.CallOption) (*GetUserMessageReply, error) {
 	var out GetUserMessageReply
-	path := binding.EncodeURL("/v1/message/user/{id}/{count}", in, true)
+	pattern := "/v1/message/user/{id}/{count}"
+	path := binding.EncodeURL(pattern, in, true)
 	opts = append(opts, http.Operation("/api.message.v1.MessageService/GetUserMessage"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "GET", path, in, &out, opts...)
 	if err != nil {
 		return nil, err
diff --git a/examples/traces/api/user/user_http.pb.go b/examples/traces/api/user/user_http.pb.go
index 758b4560..d5c41663 100644
--- a/examples/traces/api/user/user_http.pb.go
+++ b/examples/traces/api/user/user_http.pb.go
@@ -1,6 +1,6 @@
 // Code generated by protoc-gen-go-http. DO NOT EDIT.
 // versions:
-// protoc-gen-go-http v2.0.0-rc3
+// protoc-gen-go-http v2.0.0-rc5
 
 package v1
 
@@ -62,8 +62,10 @@ func NewUserHTTPClient(client *http.Client) UserHTTPClient {
 
 func (c *UserHTTPClientImpl) GetMyMessages(ctx context.Context, in *GetMyMessagesRequest, opts ...http.CallOption) (*GetMyMessagesReply, error) {
 	var out GetMyMessagesReply
-	path := binding.EncodeURL("/v1/user/get/message/{count}", in, true)
+	pattern := "/v1/user/get/message/{count}"
+	path := binding.EncodeURL(pattern, in, true)
 	opts = append(opts, http.Operation("/api.user.v1.User/GetMyMessages"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "GET", path, in, &out, opts...)
 	if err != nil {
 		return nil, err
diff --git a/transport/http/binding/encode.go b/transport/http/binding/encode.go
index 521be793..bc16e65e 100644
--- a/transport/http/binding/encode.go
+++ b/transport/http/binding/encode.go
@@ -19,16 +19,16 @@ import (
 )
 
 // EncodeURL encode proto message to url path.
-func EncodeURL(pathPattern string, msg proto.Message, needQuery bool) string {
+func EncodeURL(pathTemplate string, msg proto.Message, needQuery bool) string {
 	if msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) {
-		return pathPattern
+		return pathTemplate
 	}
 	reg := regexp.MustCompile(`/{[.\w]+}`)
 	if reg == nil {
-		return pathPattern
+		return pathTemplate
 	}
 	pathParams := make(map[string]struct{}, 0)
-	path := reg.ReplaceAllStringFunc(pathPattern, func(in string) string {
+	path := reg.ReplaceAllStringFunc(pathTemplate, func(in string) string {
 		if len(in) < 4 {
 			return in
 		}
diff --git a/transport/http/calloption.go b/transport/http/calloption.go
index cd2cd72c..db2a75db 100644
--- a/transport/http/calloption.go
+++ b/transport/http/calloption.go
@@ -13,8 +13,9 @@ type CallOption interface {
 }
 
 type callInfo struct {
-	contentType string
-	operation   string
+	contentType  string
+	operation    string
+	pathTemplate string
 }
 
 // EmptyCallOption does not alter the Call configuration.
@@ -45,8 +46,9 @@ func (o ContentTypeCallOption) before(c *callInfo) error {
 
 func defaultCallInfo(path string) callInfo {
 	return callInfo{
-		contentType: "application/json",
-		operation:   path,
+		contentType:  "application/json",
+		operation:    path,
+		pathTemplate: path,
 	}
 }
 
@@ -65,3 +67,19 @@ func (o OperationCallOption) before(c *callInfo) error {
 	c.operation = o.Operation
 	return nil
 }
+
+// PathTemplate is http path template
+func PathTemplate(pattern string) CallOption {
+	return PathTemplateCallOption{Pattern: pattern}
+}
+
+// PathTemplateCallOption is set path template for client call
+type PathTemplateCallOption struct {
+	EmptyCallOption
+	Pattern string
+}
+
+func (o PathTemplateCallOption) before(c *callInfo) error {
+	c.pathTemplate = o.Pattern
+	return nil
+}
diff --git a/transport/http/client.go b/transport/http/client.go
index a21557e9..e5aa07f4 100644
--- a/transport/http/client.go
+++ b/transport/http/client.go
@@ -197,10 +197,11 @@ func (client *Client) Invoke(ctx context.Context, method, path string, args inte
 		req.Header.Set("User-Agent", client.opts.userAgent)
 	}
 	ctx = transport.NewClientContext(ctx, &Transport{
-		endpoint:  client.opts.endpoint,
-		header:    headerCarrier(req.Header),
-		operation: c.operation,
-		request:   req,
+		endpoint:     client.opts.endpoint,
+		header:       headerCarrier(req.Header),
+		operation:    c.operation,
+		request:      req,
+		pathTemplate: c.pathTemplate,
 	})
 	return client.invoke(ctx, req, args, reply, c)
 }
diff --git a/transport/http/server.go b/transport/http/server.go
index 93f31e95..f8cc7b35 100644
--- a/transport/http/server.go
+++ b/transport/http/server.go
@@ -162,11 +162,17 @@ func (s *Server) filter() mux.MiddlewareFunc {
 				ctx, cancel = context.WithTimeout(ctx, s.timeout)
 				defer cancel()
 			}
+			pathTemplate := req.URL.Path
+			if route := mux.CurrentRoute(req); route != nil {
+				// /path/123 -> /path/{id}
+				pathTemplate, _ = route.GetPathTemplate()
+			}
 			tr := &Transport{
-				endpoint:  s.endpoint.String(),
-				operation: req.RequestURI,
-				header:    headerCarrier(req.Header),
-				request:   req,
+				endpoint:     s.endpoint.String(),
+				operation:    pathTemplate,
+				header:       headerCarrier(req.Header),
+				request:      req,
+				pathTemplate: pathTemplate,
 			}
 			if r := mux.CurrentRoute(req); r != nil {
 				if path, err := r.GetPathTemplate(); err == nil {
diff --git a/transport/http/transport.go b/transport/http/transport.go
index 743cd316..4080ee4b 100644
--- a/transport/http/transport.go
+++ b/transport/http/transport.go
@@ -13,10 +13,11 @@ var (
 
 // Transport is an HTTP transport.
 type Transport struct {
-	endpoint  string
-	operation string
-	header    headerCarrier
-	request   *http.Request
+	endpoint     string
+	operation    string
+	header       headerCarrier
+	request      *http.Request
+	pathTemplate string
 }
 
 // Kind returns the transport kind.
@@ -44,6 +45,11 @@ func (tr *Transport) Request() *http.Request {
 	return tr.request
 }
 
+// PathTemplate returns the http path template.
+func (tr *Transport) PathTemplate() string {
+	return tr.pathTemplate
+}
+
 // SetOperation sets the transport operation.
 func SetOperation(ctx context.Context, op string) {
 	if tr, ok := transport.FromServerContext(ctx); ok {


Test patch:
diff --git a/internal/testproto/echo_service_http.pb.go b/internal/testproto/echo_service_http.pb.go
index 819db2c8..c12452fd 100644
--- a/internal/testproto/echo_service_http.pb.go
+++ b/internal/testproto/echo_service_http.pb.go
@@ -1,6 +1,6 @@
 // Code generated by protoc-gen-go-http. DO NOT EDIT.
 // versions:
-// protoc-gen-go-http v2.0.0-rc3
+// protoc-gen-go-http v2.0.0-rc5
 
 package testproto
 
@@ -245,8 +245,10 @@ func NewEchoServiceHTTPClient(client *http.Client) EchoServiceHTTPClient {
 
 func (c *EchoServiceHTTPClientImpl) Echo(ctx context.Context, in *SimpleMessage, opts ...http.CallOption) (*SimpleMessage, error) {
 	var out SimpleMessage
-	path := binding.EncodeURL("/v1/example/echo/{id}", in, false)
+	pattern := "/v1/example/echo/{id}"
+	path := binding.EncodeURL(pattern, in, false)
 	opts = append(opts, http.Operation("/testproto.EchoService/Echo"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "POST", path, nil, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -256,8 +258,10 @@ func (c *EchoServiceHTTPClientImpl) Echo(ctx context.Context, in *SimpleMessage,
 
 func (c *EchoServiceHTTPClientImpl) EchoBody(ctx context.Context, in *SimpleMessage, opts ...http.CallOption) (*SimpleMessage, error) {
 	var out SimpleMessage
-	path := binding.EncodeURL("/v1/example/echo_body", in, false)
+	pattern := "/v1/example/echo_body"
+	path := binding.EncodeURL(pattern, in, false)
 	opts = append(opts, http.Operation("/testproto.EchoService/EchoBody"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -267,8 +271,10 @@ func (c *EchoServiceHTTPClientImpl) EchoBody(ctx context.Context, in *SimpleMess
 
 func (c *EchoServiceHTTPClientImpl) EchoDelete(ctx context.Context, in *SimpleMessage, opts ...http.CallOption) (*SimpleMessage, error) {
 	var out SimpleMessage
-	path := binding.EncodeURL("/v1/example/echo_delete/{id}/{num}", in, false)
+	pattern := "/v1/example/echo_delete/{id}/{num}"
+	path := binding.EncodeURL(pattern, in, false)
 	opts = append(opts, http.Operation("/testproto.EchoService/EchoDelete"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "DELETE", path, nil, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -278,8 +284,10 @@ func (c *EchoServiceHTTPClientImpl) EchoDelete(ctx context.Context, in *SimpleMe
 
 func (c *EchoServiceHTTPClientImpl) EchoPatch(ctx context.Context, in *DynamicMessageUpdate, opts ...http.CallOption) (*DynamicMessageUpdate, error) {
 	var out DynamicMessageUpdate
-	path := binding.EncodeURL("/v1/example/echo_patch", in, false)
+	pattern := "/v1/example/echo_patch"
+	path := binding.EncodeURL(pattern, in, false)
 	opts = append(opts, http.Operation("/testproto.EchoService/EchoPatch"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "PATCH", path, in.Body, &out, opts...)
 	if err != nil {
 		return nil, err
@@ -289,8 +297,10 @@ func (c *EchoServiceHTTPClientImpl) EchoPatch(ctx context.Context, in *DynamicMe
 
 func (c *EchoServiceHTTPClientImpl) EchoResponseBody(ctx context.Context, in *DynamicMessageUpdate, opts ...http.CallOption) (*DynamicMessageUpdate, error) {
 	var out DynamicMessageUpdate
-	path := binding.EncodeURL("/v1/example/echo_response_body", in, false)
+	pattern := "/v1/example/echo_response_body"
+	path := binding.EncodeURL(pattern, in, false)
 	opts = append(opts, http.Operation("/testproto.EchoService/EchoResponseBody"))
+	opts = append(opts, http.PathTemplate(pattern))
 	err := c.cc.Invoke(ctx, "POST", path, in, &out.Body, opts...)
 	if err != nil {
 		return nil, err