Repository: go-kratos/kratos
Pull request #947 (2021-05-25 05:21:10)

add path for rpc stub
get: /xxx/{name}?encode(args)
post: /xxx/{name}
-body: encode(args) / encode(args.body)
-response_body: encode(reply) / encode(reply.response_body)


Patch:
diff --git a/api/metadata/metadata.pb.go b/api/metadata/metadata.pb.go
index a5e2067b..a0160e78 100644
--- a/api/metadata/metadata.pb.go
+++ b/api/metadata/metadata.pb.go
@@ -2,7 +2,7 @@
 // versions:
 // 	protoc-gen-go v1.26.0
 // 	protoc        v3.14.0
-// source: metadata.proto
+// source: metadata/metadata.proto
 
 package metadata
 
@@ -31,7 +31,7 @@ type ListServicesRequest struct {
 func (x *ListServicesRequest) Reset() {
 	*x = ListServicesRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metadata_proto_msgTypes[0]
+		mi := &file_metadata_metadata_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -44,7 +44,7 @@ func (x *ListServicesRequest) String() string {
 func (*ListServicesRequest) ProtoMessage() {}
 
 func (x *ListServicesRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_metadata_proto_msgTypes[0]
+	mi := &file_metadata_metadata_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -57,7 +57,7 @@ func (x *ListServicesRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListServicesRequest.ProtoReflect.Descriptor instead.
 func (*ListServicesRequest) Descriptor() ([]byte, []int) {
-	return file_metadata_proto_rawDescGZIP(), []int{0}
+	return file_metadata_metadata_proto_rawDescGZIP(), []int{0}
 }
 
 type ListServicesReply struct {
@@ -72,7 +72,7 @@ type ListServicesReply struct {
 func (x *ListServicesReply) Reset() {
 	*x = ListServicesReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metadata_proto_msgTypes[1]
+		mi := &file_metadata_metadata_proto_msgTypes[1]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -85,7 +85,7 @@ func (x *ListServicesReply) String() string {
 func (*ListServicesReply) ProtoMessage() {}
 
 func (x *ListServicesReply) ProtoReflect() protoreflect.Message {
-	mi := &file_metadata_proto_msgTypes[1]
+	mi := &file_metadata_metadata_proto_msgTypes[1]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -98,7 +98,7 @@ func (x *ListServicesReply) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListServicesReply.ProtoReflect.Descriptor instead.
 func (*ListServicesReply) Descriptor() ([]byte, []int) {
-	return file_metadata_proto_rawDescGZIP(), []int{1}
+	return file_metadata_metadata_proto_rawDescGZIP(), []int{1}
 }
 
 func (x *ListServicesReply) GetServices() []string {
@@ -126,7 +126,7 @@ type GetServiceDescRequest struct {
 func (x *GetServiceDescRequest) Reset() {
 	*x = GetServiceDescRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metadata_proto_msgTypes[2]
+		mi := &file_metadata_metadata_proto_msgTypes[2]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -139,7 +139,7 @@ func (x *GetServiceDescRequest) String() string {
 func (*GetServiceDescRequest) ProtoMessage() {}
 
 func (x *GetServiceDescRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_metadata_proto_msgTypes[2]
+	mi := &file_metadata_metadata_proto_msgTypes[2]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -152,7 +152,7 @@ func (x *GetServiceDescRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetServiceDescRequest.ProtoReflect.Descriptor instead.
 func (*GetServiceDescRequest) Descriptor() ([]byte, []int) {
-	return file_metadata_proto_rawDescGZIP(), []int{2}
+	return file_metadata_metadata_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *GetServiceDescRequest) GetName() string {
@@ -173,7 +173,7 @@ type GetServiceDescReply struct {
 func (x *GetServiceDescReply) Reset() {
 	*x = GetServiceDescReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metadata_proto_msgTypes[3]
+		mi := &file_metadata_metadata_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -186,7 +186,7 @@ func (x *GetServiceDescReply) String() string {
 func (*GetServiceDescReply) ProtoMessage() {}
 
 func (x *GetServiceDescReply) ProtoReflect() protoreflect.Message {
-	mi := &file_metadata_proto_msgTypes[3]
+	mi := &file_metadata_metadata_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -199,7 +199,7 @@ func (x *GetServiceDescReply) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetServiceDescReply.ProtoReflect.Descriptor instead.
 func (*GetServiceDescReply) Descriptor() ([]byte, []int) {
-	return file_metadata_proto_rawDescGZIP(), []int{3}
+	return file_metadata_metadata_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *GetServiceDescReply) GetFileDescSet() *descriptorpb.FileDescriptorSet {
@@ -209,74 +209,74 @@ func (x *GetServiceDescReply) GetFileDescSet() *descriptorpb.FileDescriptorSet {
 	return nil
 }
 
-var File_metadata_proto protoreflect.FileDescriptor
-
-var file_metadata_proto_rawDesc = []byte{
-	0x0a, 0x0e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x12, 0x0a, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x20, 0x67, 0x6f,
-	0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65,
-	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c,
-	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74,
-	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x15, 0x0a, 0x13,
-	0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76,
-	0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76,
-	0x69, 0x63, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18,
-	0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x22, 0x2b,
-	0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x13, 0x47,
-	0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x70,
-	0x6c, 0x79, 0x12, 0x46, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x5f,
-	0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
-	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65,
-	0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x52, 0x0b, 0x66,
-	0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x53, 0x65, 0x74, 0x32, 0xdd, 0x01, 0x0a, 0x08, 0x4d,
-	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x61, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53,
-	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73,
-	0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
-	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f,
-	0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x12,
-	0x09, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0e, 0x47, 0x65,
-	0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x12, 0x21, 0x2e, 0x6b,
-	0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72,
-	0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74,
-	0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79,
-	0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, 0x63, 0x0a, 0x15, 0x63, 0x6f,
-	0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e,
-	0x61, 0x70, 0x69, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
-	0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74,
-	0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
-	0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x6d, 0x65, 0x74, 0x61, 0x64,
-	0x61, 0x74, 0x61, 0xa2, 0x02, 0x09, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x41, 0x50, 0x49, 0x62,
-	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+var File_metadata_metadata_proto protoreflect.FileDescriptor
+
+var file_metadata_metadata_proto_rawDesc = []byte{
+	0x0a, 0x17, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64,
+	0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6b, 0x72, 0x61, 0x74, 0x6f,
+	0x73, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
+	0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
+	0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x11,
+	0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c,
+	0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x18, 0x0a,
+	0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07,
+	0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x22, 0x2b, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x65,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x46, 0x0a, 0x0d, 0x66,
+	0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+	0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x52, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63,
+	0x53, 0x65, 0x74, 0x32, 0xdd, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x12, 0x61, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
+	0x12, 0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69,
+	0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x1d, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c,
+	0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79,
+	0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x44, 0x65, 0x73, 0x63, 0x12, 0x21, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61,
+	0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x73,
+	0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f,
+	0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+	0x44, 0x65, 0x73, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02,
+	0x12, 0x12, 0x10, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x6e, 0x61,
+	0x6d, 0x65, 0x7d, 0x42, 0x63, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75,
+	0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x50, 0x01, 0x5a, 0x3c,
+	0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72,
+	0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x61,
+	0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f,
+	0x61, 0x70, 0x69, 0x3b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xa2, 0x02, 0x09, 0x4b,
+	0x72, 0x61, 0x74, 0x6f, 0x73, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
-	file_metadata_proto_rawDescOnce sync.Once
-	file_metadata_proto_rawDescData = file_metadata_proto_rawDesc
+	file_metadata_metadata_proto_rawDescOnce sync.Once
+	file_metadata_metadata_proto_rawDescData = file_metadata_metadata_proto_rawDesc
 )
 
-func file_metadata_proto_rawDescGZIP() []byte {
-	file_metadata_proto_rawDescOnce.Do(func() {
-		file_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_metadata_proto_rawDescData)
+func file_metadata_metadata_proto_rawDescGZIP() []byte {
+	file_metadata_metadata_proto_rawDescOnce.Do(func() {
+		file_metadata_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_metadata_metadata_proto_rawDescData)
 	})
-	return file_metadata_proto_rawDescData
+	return file_metadata_metadata_proto_rawDescData
 }
 
-var file_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
-var file_metadata_proto_goTypes = []interface{}{
+var file_metadata_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_metadata_metadata_proto_goTypes = []interface{}{
 	(*ListServicesRequest)(nil),            // 0: kratos.api.ListServicesRequest
 	(*ListServicesReply)(nil),              // 1: kratos.api.ListServicesReply
 	(*GetServiceDescRequest)(nil),          // 2: kratos.api.GetServiceDescRequest
 	(*GetServiceDescReply)(nil),            // 3: kratos.api.GetServiceDescReply
 	(*descriptorpb.FileDescriptorSet)(nil), // 4: google.protobuf.FileDescriptorSet
 }
-var file_metadata_proto_depIdxs = []int32{
+var file_metadata_metadata_proto_depIdxs = []int32{
 	4, // 0: kratos.api.GetServiceDescReply.file_desc_set:type_name -> google.protobuf.FileDescriptorSet
 	0, // 1: kratos.api.Metadata.ListServices:input_type -> kratos.api.ListServicesRequest
 	2, // 2: kratos.api.Metadata.GetServiceDesc:input_type -> kratos.api.GetServiceDescRequest
@@ -289,13 +289,13 @@ var file_metadata_proto_depIdxs = []int32{
 	0, // [0:1] is the sub-list for field type_name
 }
 
-func init() { file_metadata_proto_init() }
-func file_metadata_proto_init() {
-	if File_metadata_proto != nil {
+func init() { file_metadata_metadata_proto_init() }
+func file_metadata_metadata_proto_init() {
+	if File_metadata_metadata_proto != nil {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_metadata_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*ListServicesRequest); i {
 			case 0:
 				return &v.state
@@ -307,7 +307,7 @@ func file_metadata_proto_init() {
 				return nil
 			}
 		}
-		file_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_metadata_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*ListServicesReply); i {
 			case 0:
 				return &v.state
@@ -319,7 +319,7 @@ func file_metadata_proto_init() {
 				return nil
 			}
 		}
-		file_metadata_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_metadata_metadata_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GetServiceDescRequest); i {
 			case 0:
 				return &v.state
@@ -331,7 +331,7 @@ func file_metadata_proto_init() {
 				return nil
 			}
 		}
-		file_metadata_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_metadata_metadata_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GetServiceDescReply); i {
 			case 0:
 				return &v.state
@@ -348,18 +348,18 @@ func file_metadata_proto_init() {
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_metadata_proto_rawDesc,
+			RawDescriptor: file_metadata_metadata_proto_rawDesc,
 			NumEnums:      0,
 			NumMessages:   4,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
-		GoTypes:           file_metadata_proto_goTypes,
-		DependencyIndexes: file_metadata_proto_depIdxs,
-		MessageInfos:      file_metadata_proto_msgTypes,
+		GoTypes:           file_metadata_metadata_proto_goTypes,
+		DependencyIndexes: file_metadata_metadata_proto_depIdxs,
+		MessageInfos:      file_metadata_metadata_proto_msgTypes,
 	}.Build()
-	File_metadata_proto = out.File
-	file_metadata_proto_rawDesc = nil
-	file_metadata_proto_goTypes = nil
-	file_metadata_proto_depIdxs = nil
+	File_metadata_metadata_proto = out.File
+	file_metadata_metadata_proto_rawDesc = nil
+	file_metadata_metadata_proto_goTypes = nil
+	file_metadata_metadata_proto_depIdxs = nil
 }
diff --git a/api/metadata/metadata_grpc.pb.go b/api/metadata/metadata_grpc.pb.go
index c110fe06..eeafa035 100644
--- a/api/metadata/metadata_grpc.pb.go
+++ b/api/metadata/metadata_grpc.pb.go
@@ -137,5 +137,5 @@ var Metadata_ServiceDesc = grpc.ServiceDesc{
 		},
 	},
 	Streams:  []grpc.StreamDesc{},
-	Metadata: "metadata.proto",
+	Metadata: "metadata/metadata.proto",
 }
diff --git a/api/metadata/metadata_http.pb.go b/api/metadata/metadata_http.pb.go
index d4f6f3d1..c610aa2c 100644
--- a/api/metadata/metadata_http.pb.go
+++ b/api/metadata/metadata_http.pb.go
@@ -26,11 +26,102 @@ 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.Handle("/services", http1.NewHandler(srv.ListServices, opts...)).Methods("GET")
+	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/{name}", http1.NewHandler(srv.GetServiceDesc, 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.BindVars(mux.Vars(r), &in); 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")
 
 	return r
 }
+
+type MetadataHttpClient interface {
+	GetServiceDesc(ctx context.Context, req *GetServiceDescRequest, opts ...http1.CallOption) (rsp *GetServiceDescReply, err error)
+
+	ListServices(ctx context.Context, req *ListServicesRequest, opts ...http1.CallOption) (rsp *ListServicesReply, err error)
+}
+
+type MetadataHttpClientImpl struct {
+	cc *http1.Client
+}
+
+func NewMetadataHttpClient(client *http1.Client) MetadataHttpClient {
+	return &MetadataHttpClientImpl{client}
+}
+
+func (c *MetadataHttpClientImpl) GetServiceDesc(ctx context.Context, in *GetServiceDescRequest, opts ...http1.CallOption) (out *GetServiceDescReply, err error) {
+	path := binding.EncodePath("GET", "/services/{name}", in)
+	out = &GetServiceDescReply{}
+
+	err = c.cc.Invoke(ctx, path, nil, &out, http1.Method("GET"), http1.PathPattern("/services/{name}"))
+
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *MetadataHttpClientImpl) ListServices(ctx context.Context, in *ListServicesRequest, opts ...http1.CallOption) (out *ListServicesReply, err error) {
+	path := binding.EncodePath("GET", "/services", in)
+	out = &ListServicesReply{}
+
+	err = c.cc.Invoke(ctx, path, nil, &out, http1.Method("GET"), http1.PathPattern("/services"))
+
+	if err != nil {
+		return
+	}
+	return
+}
diff --git a/cmd/protoc-gen-go-http/http.go b/cmd/protoc-gen-go-http/http.go
index 46b87c53..1f8a6314 100644
--- a/cmd/protoc-gen-go-http/http.go
+++ b/cmd/protoc-gen-go-http/http.go
@@ -114,14 +114,18 @@ func buildHTTPRule(g *protogen.GeneratedFile, m *protogen.Method, rule *annotati
 	responseBody = rule.ResponseBody
 	md := buildMethodDesc(g, m, method, path)
 	if body == "*" {
-		md.Body = "*"
+		md.HasBody = true
+		md.Body = ""
 	} else if body != "" {
-		md.Body = "." + body
+		md.HasBody = true
+		md.Body = "." + camelCaseVars(body)
+	} else {
+		md.HasBody = false
 	}
 	if responseBody == "*" {
-		md.ResponseBody = "*"
+		md.ResponseBody = ""
 	} else if responseBody != "" {
-		md.ResponseBody = "." + responseBody
+		md.ResponseBody = "." + camelCaseVars(responseBody)
 	}
 	return md
 }
diff --git a/cmd/protoc-gen-go-http/template.go b/cmd/protoc-gen-go-http/template.go
index 58553c27..d37eee4c 100644
--- a/cmd/protoc-gen-go-http/template.go
+++ b/cmd/protoc-gen-go-http/template.go
@@ -14,9 +14,40 @@ 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.Handle("{{.Path}}", http1.NewHandler(srv.{{.Name}}, opts...)).Methods("{{.Method}}")
+	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.BindVars(mux.Vars(r), &in); 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}}")
 	{{end}}
 	return r
 }
@@ -39,12 +70,13 @@ func New{{.ServiceType}}HttpClient (client *http1.Client) {{.ServiceType}}HttpCl
 {{$svrName := .ServiceName}}
 {{range .MethodSets}}
 func (c *{{$svrType}}HttpClientImpl) {{.Name}}(ctx context.Context, in *{{.Request}}, opts ...http1.CallOption) (out *{{.Reply}}, err error) {
-	path := "{{.Path}}"
-	method := "{{.Method}}"
-	body := "{{.Body}}"
-	
+	path := binding.EncodePath("{{.Method}}", "{{.Path}}", in)
 	out = &{{.Reply}}{}
-	err = c.cc.Invoke(ctx, path, in, out, http1.BodyPattern(body), http1.Method(method))
+	{{if .HasBody }}
+	err = c.cc.Invoke(ctx, path, in{{.Body}}, &out{{.ResponseBody}}, http1.Method("{{.Method}}"), http1.PathPattern("{{.Path}}"))
+	{{else}} 
+	err = c.cc.Invoke(ctx, path, nil, &out{{.ResponseBody}}, http1.Method("{{.Method}}"), http1.PathPattern("{{.Path}}"))
+	{{end}}
 	if err != nil {
 		return
 	}
@@ -66,12 +98,12 @@ type methodDesc struct {
 	Name    string
 	Num     int
 	Vars    []string
-	Forms   []string
 	Request string
 	Reply   string
 	// http_rule
 	Path         string
 	Method       string
+	HasBody      bool
 	Body         string
 	ResponseBody string
 }
diff --git a/examples/helloworld/helloworld/helloworld_http.pb.go b/examples/helloworld/helloworld/helloworld_http.pb.go
index 0bf9fea1..c3ec46ca 100644
--- a/examples/helloworld/helloworld/helloworld_http.pb.go
+++ b/examples/helloworld/helloworld/helloworld_http.pb.go
@@ -24,9 +24,40 @@ 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.Handle("/helloworld/{name}", http1.NewHandler(srv.SayHello, opts...)).Methods("GET")
+	r.HandleFunc("/helloworld/{name}", func(w http.ResponseWriter, r *http.Request) {
+		var in HelloRequest
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := binding.BindVars(mux.Vars(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
+		}
+		reply := out.(*HelloReply)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
 
 	return r
 }
@@ -44,12 +75,11 @@ func NewGreeterHttpClient(client *http1.Client) GreeterHttpClient {
 }
 
 func (c *GreeterHttpClientImpl) SayHello(ctx context.Context, in *HelloRequest, opts ...http1.CallOption) (out *HelloReply, err error) {
-	path := "/helloworld/{name}"
-	method := "GET"
-	body := ""
-
+	path := binding.EncodePath("GET", "/helloworld/{name}", in)
 	out = &HelloReply{}
-	err = c.cc.Invoke(ctx, path, in, out, http1.BodyPattern(body), http1.Method(method))
+
+	err = c.cc.Invoke(ctx, path, nil, &out, http1.Method("GET"), http1.PathPattern("/helloworld/{name}"))
+
 	if err != nil {
 		return
 	}
diff --git a/examples/http/handler/main.go b/examples/http/handler/main.go
index c65ae9e0..53913bec 100644
--- a/examples/http/handler/main.go
+++ b/examples/http/handler/main.go
@@ -34,8 +34,8 @@ func main() {
 		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")
+	router.Handle("/users/add", http.NewHandler(us.Add)).Methods("POST")
+	router.Handle("/users/detail", http.NewHandler(us.Get)).Methods("GET")
 
 	httpSrv := http.NewServer(http.Address(":8000"))
 	httpSrv.HandlePrefix("/", router)
diff --git a/examples/proto/echo_service.pb.go b/examples/proto/echo_service.pb.go
index f8438f70..c8e37f9c 100644
--- a/examples/proto/echo_service.pb.go
+++ b/examples/proto/echo_service.pb.go
@@ -1,18 +1,17 @@
 // 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: echo_service.proto
 
 package testproto
 
 import (
-	proto "github.com/golang/protobuf/proto"
-	_struct "github.com/golang/protobuf/ptypes/struct"
 	_ "google.golang.org/genproto/googleapis/api/annotations"
-	field_mask "google.golang.org/genproto/protobuf/field_mask"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb"
+	structpb "google.golang.org/protobuf/types/known/structpb"
 	reflect "reflect"
 	sync "sync"
 )
@@ -24,10 +23,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
-
 type Corpus int32
 
 const (
@@ -332,8 +327,8 @@ type DynamicMessage struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	StructField *_struct.Struct `protobuf:"bytes,1,opt,name=struct_field,json=structField,proto3" json:"struct_field,omitempty"`
-	ValueField  *_struct.Value  `protobuf:"bytes,2,opt,name=value_field,json=valueField,proto3" json:"value_field,omitempty"`
+	StructField *structpb.Struct `protobuf:"bytes,1,opt,name=struct_field,json=structField,proto3" json:"struct_field,omitempty"`
+	ValueField  *structpb.Value  `protobuf:"bytes,2,opt,name=value_field,json=valueField,proto3" json:"value_field,omitempty"`
 }
 
 func (x *DynamicMessage) Reset() {
@@ -368,14 +363,14 @@ func (*DynamicMessage) Descriptor() ([]byte, []int) {
 	return file_echo_service_proto_rawDescGZIP(), []int{2}
 }
 
-func (x *DynamicMessage) GetStructField() *_struct.Struct {
+func (x *DynamicMessage) GetStructField() *structpb.Struct {
 	if x != nil {
 		return x.StructField
 	}
 	return nil
 }
 
-func (x *DynamicMessage) GetValueField() *_struct.Value {
+func (x *DynamicMessage) GetValueField() *structpb.Value {
 	if x != nil {
 		return x.ValueField
 	}
@@ -387,8 +382,8 @@ type DynamicMessageUpdate struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Body       *DynamicMessage       `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
-	UpdateMask *field_mask.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`
+	Body       *DynamicMessage        `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
+	UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`
 }
 
 func (x *DynamicMessageUpdate) Reset() {
@@ -430,7 +425,7 @@ func (x *DynamicMessageUpdate) GetBody() *DynamicMessage {
 	return nil
 }
 
-func (x *DynamicMessageUpdate) GetUpdateMask() *field_mask.FieldMask {
+func (x *DynamicMessageUpdate) GetUpdateMask() *fieldmaskpb.FieldMask {
 	if x != nil {
 		return x.UpdateMask
 	}
@@ -491,7 +486,7 @@ var file_echo_service_proto_rawDesc = []byte{
 	0x4d, 0x41, 0x47, 0x45, 0x53, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f, 0x43, 0x41, 0x4c,
 	0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x45, 0x57, 0x53, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08,
 	0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x53, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x56, 0x49,
-	0x44, 0x45, 0x4f, 0x10, 0x06, 0x32, 0xcc, 0x05, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x53, 0x65,
+	0x44, 0x45, 0x4f, 0x10, 0x06, 0x32, 0xcf, 0x05, 0x0a, 0x0b, 0x45, 0x63, 0x68, 0x6f, 0x53, 0x65,
 	0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xf2, 0x01, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x18,
 	0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c,
 	0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70,
@@ -513,35 +508,36 @@ var file_echo_service_proto_rawDesc = []byte{
 	0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d,
 	0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93,
 	0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f,
-	0x65, 0x63, 0x68, 0x6f, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a,
+	0x65, 0x63, 0x68, 0x6f, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x85, 0x01, 0x0a,
 	0x10, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x6f, 0x64,
 	0x79, 0x12, 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79,
 	0x6e, 0x61, 0x6d, 0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61,
 	0x74, 0x65, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44,
 	0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64,
-	0x61, 0x74, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x22, 0x1e, 0x2f, 0x76, 0x31,
+	0x61, 0x74, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31,
 	0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x72, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x62, 0x04, 0x62, 0x6f, 0x64,
-	0x79, 0x12, 0x6c, 0x0a, 0x0a, 0x45, 0x63, 0x68, 0x6f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12,
-	0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70,
-	0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73,
-	0x61, 0x67, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x2a, 0x22, 0x2f, 0x76, 0x31,
-	0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x64, 0x65,
-	0x6c, 0x65, 0x74, 0x65, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x6e, 0x75, 0x6d, 0x7d, 0x12,
-	0x73, 0x0a, 0x09, 0x45, 0x63, 0x68, 0x6f, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x74,
-	0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63,
-	0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x1f, 0x2e,
-	0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69,
-	0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x24,
-	0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d,
-	0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x04,
-	0x62, 0x6f, 0x64, 0x79, 0x42, 0x51, 0x5a, 0x4f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
-	0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61,
-	0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67,
-	0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
-	0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x74, 0x65,
-	0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x3a, 0x01, 0x2a, 0x62, 0x04,
+	0x62, 0x6f, 0x64, 0x79, 0x12, 0x6c, 0x0a, 0x0a, 0x45, 0x63, 0x68, 0x6f, 0x44, 0x65, 0x6c, 0x65,
+	0x74, 0x65, 0x12, 0x18, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53,
+	0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x74,
+	0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4d,
+	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x2a, 0x22,
+	0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f,
+	0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x6e, 0x75,
+	0x6d, 0x7d, 0x12, 0x73, 0x0a, 0x09, 0x45, 0x63, 0x68, 0x6f, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12,
+	0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79, 0x6e, 0x61,
+	0x6d, 0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+	0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x79, 0x6e,
+	0x61, 0x6d, 0x69, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74,
+	0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x65,
+	0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x63,
+	0x68, 0x3a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x51, 0x5a, 0x4f, 0x67, 0x69, 0x74, 0x68, 0x75,
+	0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f,
+	0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x3b, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
 }
 
 var (
@@ -559,14 +555,14 @@ func file_echo_service_proto_rawDescGZIP() []byte {
 var file_echo_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_echo_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
 var file_echo_service_proto_goTypes = []interface{}{
-	(Corpus)(0),                  // 0: testproto.Corpus
-	(*Embedded)(nil),             // 1: testproto.Embedded
-	(*SimpleMessage)(nil),        // 2: testproto.SimpleMessage
-	(*DynamicMessage)(nil),       // 3: testproto.DynamicMessage
-	(*DynamicMessageUpdate)(nil), // 4: testproto.DynamicMessageUpdate
-	(*_struct.Struct)(nil),       // 5: google.protobuf.Struct
-	(*_struct.Value)(nil),        // 6: google.protobuf.Value
-	(*field_mask.FieldMask)(nil), // 7: google.protobuf.FieldMask
+	(Corpus)(0),                   // 0: testproto.Corpus
+	(*Embedded)(nil),              // 1: testproto.Embedded
+	(*SimpleMessage)(nil),         // 2: testproto.SimpleMessage
+	(*DynamicMessage)(nil),        // 3: testproto.DynamicMessage
+	(*DynamicMessageUpdate)(nil),  // 4: testproto.DynamicMessageUpdate
+	(*structpb.Struct)(nil),       // 5: google.protobuf.Struct
+	(*structpb.Value)(nil),        // 6: google.protobuf.Value
+	(*fieldmaskpb.FieldMask)(nil), // 7: google.protobuf.FieldMask
 }
 var file_echo_service_proto_depIdxs = []int32{
 	1,  // 0: testproto.SimpleMessage.status:type_name -> testproto.Embedded
diff --git a/examples/proto/echo_service.proto b/examples/proto/echo_service.proto
index a0a6b743..ed084156 100644
--- a/examples/proto/echo_service.proto
+++ b/examples/proto/echo_service.proto
@@ -89,6 +89,7 @@ service EchoService {
 	rpc EchoResponseBody(DynamicMessageUpdate) returns (DynamicMessageUpdate) {
 		option (google.api.http) = {
 			post: "/v1/example/echo_response_body"
+			body: "*"
 			response_body: "body"
 		};
 	}
diff --git a/examples/proto/echo_service_http.pb.go b/examples/proto/echo_service_http.pb.go
index 438314f3..5ee31378 100644
--- a/examples/proto/echo_service_http.pb.go
+++ b/examples/proto/echo_service_http.pb.go
@@ -32,25 +32,257 @@ type EchoServiceHandler interface {
 }
 
 func NewEchoServiceHandler(srv EchoServiceHandler, opts ...http1.HandleOption) http.Handler {
+	h := http1.DefaultHandleOptions()
+	for _, o := range opts {
+		o(&h)
+	}
 	r := mux.NewRouter()
 
-	r.Handle("/v1/example/echo/{id}/{num}", http1.NewHandler(srv.Echo, opts...)).Methods("GET")
-
-	r.Handle("/v1/example/echo/{id}/{num}/{lang}", http1.NewHandler(srv.Echo, opts...)).Methods("GET")
-
-	r.Handle("/v1/example/echo1/{id}/{line_num}/{status.note}", http1.NewHandler(srv.Echo, opts...)).Methods("GET")
-
-	r.Handle("/v1/example/echo2/{no.note}", http1.NewHandler(srv.Echo, opts...)).Methods("GET")
-
-	r.Handle("/v1/example/echo/{id}", http1.NewHandler(srv.Echo, opts...)).Methods("POST")
-
-	r.Handle("/v1/example/echo_body", http1.NewHandler(srv.EchoBody, opts...)).Methods("POST")
-
-	r.Handle("/v1/example/echo_response_body", http1.NewHandler(srv.EchoResponseBody, opts...)).Methods("POST")
-
-	r.Handle("/v1/example/echo_delete/{id}/{num}", http1.NewHandler(srv.EchoDelete, opts...)).Methods("DELETE")
-
-	r.Handle("/v1/example/echo_patch", http1.NewHandler(srv.EchoPatch, opts...)).Methods("PATCH")
+	r.HandleFunc("/v1/example/echo/{id}/{num}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := binding.BindVars(mux.Vars(r), &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		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.(*SimpleMessage)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
+
+	r.HandleFunc("/v1/example/echo/{id}/{num}/{lang}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := binding.BindVars(mux.Vars(r), &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		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.(*SimpleMessage)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
+
+	r.HandleFunc("/v1/example/echo1/{id}/{line_num}/{status.note}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := binding.BindVars(mux.Vars(r), &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		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.(*SimpleMessage)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
+
+	r.HandleFunc("/v1/example/echo2/{no.note}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := binding.BindVars(mux.Vars(r), &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		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.(*SimpleMessage)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("GET")
+
+	r.HandleFunc("/v1/example/echo/{id}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := binding.BindVars(mux.Vars(r), &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.Echo(ctx, req.(*SimpleMessage))
+		}
+		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.(*SimpleMessage)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("POST")
+
+	r.HandleFunc("/v1/example/echo_body", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+		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.EchoBody(ctx, req.(*SimpleMessage))
+		}
+		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.(*SimpleMessage)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("POST")
+
+	r.HandleFunc("/v1/example/echo_response_body", func(w http.ResponseWriter, r *http.Request) {
+		var in DynamicMessageUpdate
+		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.EchoResponseBody(ctx, req.(*DynamicMessageUpdate))
+		}
+		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.(*DynamicMessageUpdate)
+		if err := h.Encode(w, r, reply.Body); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("POST")
+
+	r.HandleFunc("/v1/example/echo_delete/{id}/{num}", func(w http.ResponseWriter, r *http.Request) {
+		var in SimpleMessage
+		if err := h.Decode(r, &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		if err := binding.BindVars(mux.Vars(r), &in); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.EchoDelete(ctx, req.(*SimpleMessage))
+		}
+		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.(*SimpleMessage)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("DELETE")
+
+	r.HandleFunc("/v1/example/echo_patch", func(w http.ResponseWriter, r *http.Request) {
+		var in DynamicMessageUpdate
+		if err := h.Decode(r, &in.Body); err != nil {
+			h.Error(w, r, err)
+			return
+		}
+
+		next := func(ctx context.Context, req interface{}) (interface{}, error) {
+			return srv.EchoPatch(ctx, req.(*DynamicMessageUpdate))
+		}
+		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.(*DynamicMessageUpdate)
+		if err := h.Encode(w, r, reply); err != nil {
+			h.Error(w, r, err)
+		}
+	}).Methods("PATCH")
 
 	return r
 }
@@ -76,12 +308,11 @@ func NewEchoServiceHttpClient(client *http1.Client) EchoServiceHttpClient {
 }
 
 func (c *EchoServiceHttpClientImpl) Echo(ctx context.Context, in *SimpleMessage, opts ...http1.CallOption) (out *SimpleMessage, err error) {
-	path := "/v1/example/echo/{id}"
-	method := "POST"
-	body := ""
-
+	path := binding.EncodePath("POST", "/v1/example/echo/{id}", in)
 	out = &SimpleMessage{}
-	err = c.cc.Invoke(ctx, path, in, out, http1.BodyPattern(body), http1.Method(method))
+
+	err = c.cc.Invoke(ctx, path, nil, &out, http1.Method("POST"), http1.PathPattern("/v1/example/echo/{id}"))
+
 	if err != nil {
 		return
 	}
@@ -89,12 +320,11 @@ func (c *EchoServiceHttpClientImpl) Echo(ctx context.Context, in *SimpleMessage,
 }
 
 func (c *EchoServiceHttpClientImpl) EchoBody(ctx context.Context, in *SimpleMessage, opts ...http1.CallOption) (out *SimpleMessage, err error) {
-	path := "/v1/example/echo_body"
-	method := "POST"
-	body := "*"
-
+	path := binding.EncodePath("POST", "/v1/example/echo_body", in)
 	out = &SimpleMessage{}
-	err = c.cc.Invoke(ctx, path, in, out, http1.BodyPattern(body), http1.Method(method))
+
+	err = c.cc.Invoke(ctx, path, in, &out, http1.Method("POST"), http1.PathPattern("/v1/example/echo_body"))
+
 	if err != nil {
 		return
 	}
@@ -102,12 +332,11 @@ func (c *EchoServiceHttpClientImpl) EchoBody(ctx context.Context, in *SimpleMess
 }
 
 func (c *EchoServiceHttpClientImpl) EchoDelete(ctx context.Context, in *SimpleMessage, opts ...http1.CallOption) (out *SimpleMessage, err error) {
-	path := "/v1/example/echo_delete/{id}/{num}"
-	method := "DELETE"
-	body := ""
-
+	path := binding.EncodePath("DELETE", "/v1/example/echo_delete/{id}/{num}", in)
 	out = &SimpleMessage{}
-	err = c.cc.Invoke(ctx, path, in, out, http1.BodyPattern(body), http1.Method(method))
+
+	err = c.cc.Invoke(ctx, path, nil, &out, http1.Method("DELETE"), http1.PathPattern("/v1/example/echo_delete/{id}/{num}"))
+
 	if err != nil {
 		return
 	}
@@ -115,12 +344,11 @@ func (c *EchoServiceHttpClientImpl) EchoDelete(ctx context.Context, in *SimpleMe
 }
 
 func (c *EchoServiceHttpClientImpl) EchoPatch(ctx context.Context, in *DynamicMessageUpdate, opts ...http1.CallOption) (out *DynamicMessageUpdate, err error) {
-	path := "/v1/example/echo_patch"
-	method := "PATCH"
-	body := ".body"
-
+	path := binding.EncodePath("PATCH", "/v1/example/echo_patch", in)
 	out = &DynamicMessageUpdate{}
-	err = c.cc.Invoke(ctx, path, in, out, http1.BodyPattern(body), http1.Method(method))
+
+	err = c.cc.Invoke(ctx, path, in.Body, &out, http1.Method("PATCH"), http1.PathPattern("/v1/example/echo_patch"))
+
 	if err != nil {
 		return
 	}
@@ -128,12 +356,11 @@ func (c *EchoServiceHttpClientImpl) EchoPatch(ctx context.Context, in *DynamicMe
 }
 
 func (c *EchoServiceHttpClientImpl) EchoResponseBody(ctx context.Context, in *DynamicMessageUpdate, opts ...http1.CallOption) (out *DynamicMessageUpdate, err error) {
-	path := "/v1/example/echo_response_body"
-	method := "POST"
-	body := ""
-
+	path := binding.EncodePath("POST", "/v1/example/echo_response_body", in)
 	out = &DynamicMessageUpdate{}
-	err = c.cc.Invoke(ctx, path, in, out, http1.BodyPattern(body), http1.Method(method))
+
+	err = c.cc.Invoke(ctx, path, in, &out.Body, http1.Method("POST"), http1.PathPattern("/v1/example/echo_response_body"))
+
 	if err != nil {
 		return
 	}
diff --git a/examples/proto/stream.pb.go b/examples/proto/stream.pb.go
index a2fed91d..04d55590 100644
--- a/examples/proto/stream.pb.go
+++ b/examples/proto/stream.pb.go
@@ -1,18 +1,17 @@
 // 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: stream.proto
 
 package testproto
 
 import (
-	proto "github.com/golang/protobuf/proto"
-	empty "github.com/golang/protobuf/ptypes/empty"
 	_ "google.golang.org/genproto/googleapis/api/annotations"
 	httpbody "google.golang.org/genproto/googleapis/api/httpbody"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
 	reflect "reflect"
 )
 
@@ -23,10 +22,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
-
 var File_stream_proto protoreflect.FileDescriptor
 
 var file_stream_proto_rawDesc = []byte{
@@ -53,7 +48,7 @@ var file_stream_proto_rawDesc = []byte{
 }
 
 var file_stream_proto_goTypes = []interface{}{
-	(*empty.Empty)(nil),       // 0: google.protobuf.Empty
+	(*emptypb.Empty)(nil),     // 0: google.protobuf.Empty
 	(*httpbody.HttpBody)(nil), // 1: google.api.HttpBody
 }
 var file_stream_proto_depIdxs = []int32{
diff --git a/examples/proto/stream_grpc.pb.go b/examples/proto/stream_grpc.pb.go
index ed744d6b..9dfe96f8 100644
--- a/examples/proto/stream_grpc.pb.go
+++ b/examples/proto/stream_grpc.pb.go
@@ -4,11 +4,11 @@ package testproto
 
 import (
 	context "context"
-	empty "github.com/golang/protobuf/ptypes/empty"
 	httpbody "google.golang.org/genproto/googleapis/api/httpbody"
 	grpc "google.golang.org/grpc"
 	codes "google.golang.org/grpc/codes"
 	status "google.golang.org/grpc/status"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
 )
 
 // This is a compile-time assertion to ensure that this generated file
@@ -20,7 +20,7 @@ const _ = grpc.SupportPackageIsVersion7
 //
 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
 type StreamServiceClient interface {
-	Download(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (StreamService_DownloadClient, error)
+	Download(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (StreamService_DownloadClient, error)
 }
 
 type streamServiceClient struct {
@@ -31,7 +31,7 @@ func NewStreamServiceClient(cc grpc.ClientConnInterface) StreamServiceClient {
 	return &streamServiceClient{cc}
 }
 
-func (c *streamServiceClient) Download(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (StreamService_DownloadClient, error) {
+func (c *streamServiceClient) Download(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (StreamService_DownloadClient, error) {
 	stream, err := c.cc.NewStream(ctx, &StreamService_ServiceDesc.Streams[0], "/testproto.StreamService/Download", opts...)
 	if err != nil {
 		return nil, err
@@ -67,7 +67,7 @@ func (x *streamServiceDownloadClient) Recv() (*httpbody.HttpBody, error) {
 // All implementations must embed UnimplementedStreamServiceServer
 // for forward compatibility
 type StreamServiceServer interface {
-	Download(*empty.Empty, StreamService_DownloadServer) error
+	Download(*emptypb.Empty, StreamService_DownloadServer) error
 	mustEmbedUnimplementedStreamServiceServer()
 }
 
@@ -75,7 +75,7 @@ type StreamServiceServer interface {
 type UnimplementedStreamServiceServer struct {
 }
 
-func (UnimplementedStreamServiceServer) Download(*empty.Empty, StreamService_DownloadServer) error {
+func (UnimplementedStreamServiceServer) Download(*emptypb.Empty, StreamService_DownloadServer) error {
 	return status.Errorf(codes.Unimplemented, "method Download not implemented")
 }
 func (UnimplementedStreamServiceServer) mustEmbedUnimplementedStreamServiceServer() {}
@@ -92,7 +92,7 @@ func RegisterStreamServiceServer(s grpc.ServiceRegistrar, srv StreamServiceServe
 }
 
 func _StreamService_Download_Handler(srv interface{}, stream grpc.ServerStream) error {
-	m := new(empty.Empty)
+	m := new(emptypb.Empty)
 	if err := stream.RecvMsg(m); err != nil {
 		return err
 	}
diff --git a/examples/proto/stream_http.pb.go b/examples/proto/stream_http.pb.go
index 4ca8e97f..c1b3e335 100644
--- a/examples/proto/stream_http.pb.go
+++ b/examples/proto/stream_http.pb.go
@@ -23,6 +23,10 @@ type StreamServiceHandler interface {
 }
 
 func NewStreamServiceHandler(srv StreamServiceHandler, opts ...http1.HandleOption) http.Handler {
+	h := http1.DefaultHandleOptions()
+	for _, o := range opts {
+		o(&h)
+	}
 	r := mux.NewRouter()
 
 	return r
diff --git a/transport/http/binding/encode.go b/transport/http/binding/encode.go
new file mode 100644
index 00000000..5aa86b36
--- /dev/null
+++ b/transport/http/binding/encode.go
@@ -0,0 +1,231 @@
+package binding
+
+import (
+	"encoding/base64"
+	"fmt"
+	"net/url"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+
+	"google.golang.org/genproto/protobuf/field_mask"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/types/known/durationpb"
+	"google.golang.org/protobuf/types/known/timestamppb"
+	"google.golang.org/protobuf/types/known/wrapperspb"
+)
+
+// EncodePath binds proto message to url path
+func EncodePath(method string, pathPattern string, msg proto.Message) string {
+	if msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) {
+		return pathPattern
+	}
+	reg := regexp.MustCompile(`/{[.\w]+}`)
+	if reg == nil {
+		return pathPattern
+	}
+	pathParams := make(map[string]struct{}, 0)
+	path := reg.ReplaceAllStringFunc(pathPattern, func(in string) string {
+		if len(in) < 4 {
+			return in
+		}
+		key := in[2 : len(in)-1]
+		vars := strings.Split(key, ".")
+		if value, err := getValueByField(msg.ProtoReflect(), vars); err == nil {
+			pathParams[key] = struct{}{}
+			return "/" + value
+		}
+		return in
+	})
+	if method == "GET" {
+		u, err := encodeQuery(msg)
+		if err == nil && len(u) > 0 {
+			for key := range pathParams {
+				delete(u, key)
+			}
+			query := u.Encode()
+			if query != "" {
+				path += "?" + query
+			}
+		}
+	}
+
+	return path
+}
+
+func getValueByField(v protoreflect.Message, fieldPath []string) (string, error) {
+	var fd protoreflect.FieldDescriptor
+	for i, fieldName := range fieldPath {
+		fields := v.Descriptor().Fields()
+		if fd = fields.ByName(protoreflect.Name(fieldName)); fd == nil {
+			fd = fields.ByJSONName(fieldName)
+			if fd == nil {
+				return "", fmt.Errorf("field path not found: %q", fieldName)
+			}
+		}
+		if i == len(fieldPath)-1 {
+			break
+		}
+		if fd.Message() == nil || fd.Cardinality() == protoreflect.Repeated {
+			return "", fmt.Errorf("invalid path: %q is not a message", fieldName)
+		}
+		v = v.Get(fd).Message()
+	}
+	return encodeField(fd, v.Get(fd))
+}
+
+func encodeQuery(msg proto.Message) (url.Values, error) {
+	if msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) {
+		return url.Values{}, nil
+	}
+	u := make(url.Values)
+	err := encodeByField(u, "", msg.ProtoReflect())
+	if err != nil {
+		return nil, err
+	}
+	return u, nil
+}
+
+func encodeByField(u url.Values, path string, v protoreflect.Message) error {
+	for i := 0; i < v.Descriptor().Fields().Len(); i++ {
+		fd := v.Descriptor().Fields().Get(i)
+		var key string = fd.TextName()
+		var newPath string
+		if path == "" {
+			newPath = key
+		} else {
+			newPath = path + "." + key
+		}
+		if fd.HasJSONName() {
+			key = fd.JSONName()
+		}
+		switch {
+		case fd.IsList():
+			list, err := encodeRepeatedField(fd, v.Get(fd).List())
+			if err != nil {
+				return err
+			}
+			u[newPath] = list
+		case fd.IsMap():
+			m, err := encodeMapField(fd, v.Get(fd).Map())
+			if err != nil {
+				return err
+			}
+			for k, value := range m {
+				u[fmt.Sprintf("%s[%s]", newPath, k)] = []string{value}
+			}
+		case (fd.Kind() == protoreflect.MessageKind) || (fd.Kind() == protoreflect.GroupKind):
+			value, err := encodeMessage(fd.Message(), v.Get(fd))
+			if err == nil {
+				u[newPath] = []string{value}
+				return nil
+			}
+			return encodeByField(u, newPath, v.Get(fd).Message())
+		default:
+			value, err := encodeField(fd, v.Get(fd))
+			if err != nil {
+				return err
+			}
+			u[newPath] = []string{value}
+		}
+	}
+	return nil
+}
+
+func encodeMessageField(fieldDescriptor protoreflect.FieldDescriptor, msgValue protoreflect.Message) (string, error) {
+	return encodeField(fieldDescriptor, msgValue.Get(fieldDescriptor))
+}
+
+func encodeRepeatedField(fieldDescriptor protoreflect.FieldDescriptor, list protoreflect.List) ([]string, error) {
+	var values []string
+	for i := 0; i < list.Len(); i++ {
+		value, err := encodeField(fieldDescriptor, list.Get(i))
+		if err != nil {
+			return nil, err
+		}
+		values = append(values, value)
+	}
+
+	return values, nil
+}
+
+func encodeMapField(fieldDescriptor protoreflect.FieldDescriptor, mp protoreflect.Map) (map[string]string, error) {
+	m := make(map[string]string)
+	mp.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
+		key, err := encodeField(fieldDescriptor.MapValue(), k.Value())
+		if err != nil {
+			return false
+		}
+		value, err := encodeField(fieldDescriptor.MapValue(), v)
+		if err != nil {
+			return false
+		}
+		m[key] = value
+		return true
+	})
+
+	return m, nil
+}
+
+func encodeField(fieldDescriptor protoreflect.FieldDescriptor, value protoreflect.Value) (string, error) {
+	switch fieldDescriptor.Kind() {
+	case protoreflect.BoolKind:
+		return strconv.FormatBool(value.Bool()), nil
+	case protoreflect.EnumKind:
+		if fieldDescriptor.Enum().FullName() == "google.protobuf.NullValue" {
+			return "null", nil
+		}
+		desc := fieldDescriptor.Enum().Values().ByNumber(value.Enum())
+		return string(desc.Name()), nil
+	case protoreflect.StringKind:
+		return value.String(), nil
+	case protoreflect.BytesKind:
+		return base64.URLEncoding.EncodeToString(value.Bytes()), nil
+	case protoreflect.MessageKind, protoreflect.GroupKind:
+		return encodeMessage(fieldDescriptor.Message(), value)
+	default:
+		return fmt.Sprintf("%v", value.Interface()), nil
+	}
+}
+
+// marshalMessage marshals the fields in the given protoreflect.Message.
+// If the typeURL is non-empty, then a synthetic "@type" field is injected
+// containing the URL as the value.
+func encodeMessage(msgDescriptor protoreflect.MessageDescriptor, value protoreflect.Value) (string, error) {
+	switch msgDescriptor.FullName() {
+	case "google.protobuf.Timestamp":
+		t, ok := value.Interface().(*timestamppb.Timestamp)
+		if !ok {
+			return "", nil
+		}
+		return t.AsTime().Format(time.RFC3339Nano), nil
+	case "google.protobuf.Duration":
+		d, ok := value.Interface().(*durationpb.Duration)
+		if !ok {
+			return "", nil
+		}
+		return d.AsDuration().String(), nil
+	case "google.protobuf.BytesValue":
+		b, ok := value.Interface().(*wrapperspb.BytesValue)
+		if !ok {
+			return "", nil
+		}
+		return base64.StdEncoding.EncodeToString(b.Value), nil
+	case "google.protobuf.DoubleValue", "google.protobuf.FloatValue", "google.protobuf.Int64Value", "google.protobuf.Int32Value",
+		"google.protobuf.UInt64Value", "google.protobuf.UInt32Value", "google.protobuf.BoolValue", "google.protobuf.StringValue":
+		fd := msgDescriptor.Fields()
+		v := value.Message().Get(fd.ByName(protoreflect.Name("value"))).Message()
+		return fmt.Sprintf("%v", v.Interface()), nil
+	case "google.protobuf.FieldMask":
+		m, ok := value.Interface().(*field_mask.FieldMask)
+		if !ok {
+			return "", nil
+		}
+		return strings.Join(m.Paths, ","), nil
+	default:
+		return "", fmt.Errorf("unsupported message type: %q", string(msgDescriptor.FullName()))
+	}
+}
diff --git a/transport/http/binding/proto.go b/transport/http/binding/proto.go
index 27512147..a56d693c 100644
--- a/transport/http/binding/proto.go
+++ b/transport/http/binding/proto.go
@@ -4,7 +4,6 @@ import (
 	"encoding/base64"
 	"errors"
 	"fmt"
-	"regexp"
 	"strconv"
 	"strings"
 	"time"
@@ -18,46 +17,6 @@ import (
 	"google.golang.org/protobuf/types/known/wrapperspb"
 )
 
-// ProtoPath binds proto message to url path
-func ProtoPath(template string, msg proto.Message) string {
-	reg := regexp.MustCompile(`/{[.\w]+}`)
-	if reg == nil {
-		return template
-	}
-	return reg.ReplaceAllStringFunc(template, func(in string) string {
-		if len(in) < 4 {
-			return in
-		}
-		str := in[2 : len(in)-1]
-		vars := strings.Split(str, ".")
-		if value, err := getValueByField(msg.ProtoReflect(), vars); err == nil {
-			return "/" + value
-		}
-		return in
-	})
-}
-
-func getValueByField(v protoreflect.Message, fieldPath []string) (string, error) {
-	var fd protoreflect.FieldDescriptor
-	for i, fieldName := range fieldPath {
-		fields := v.Descriptor().Fields()
-		if fd = fields.ByName(protoreflect.Name(fieldName)); fd == nil {
-			fd = fields.ByJSONName(fieldName)
-			if fd == nil {
-				return "", fmt.Errorf("field path not found: %q", fieldName)
-			}
-		}
-		if i == len(fieldPath)-1 {
-			break
-		}
-		if fd.Message() == nil || fd.Cardinality() == protoreflect.Repeated {
-			return "", fmt.Errorf("invalid path: %q is not a message", fieldName)
-		}
-		v = v.Mutable(fd).Message()
-	}
-	return fmt.Sprintf("%v", v.Get(fd).Interface()), nil
-}
-
 // MapProto sets a value in a nested Protobuf structure.
 // Deprecated: use BindValue instead.
 func MapProto(msg proto.Message, values map[string]string) error {
diff --git a/transport/http/calloption.go b/transport/http/calloption.go
index 2920ead6..b794859b 100644
--- a/transport/http/calloption.go
+++ b/transport/http/calloption.go
@@ -12,6 +12,11 @@ type CallOption interface {
 	after(*callInfo, *csAttempt)
 }
 
+type callInfo struct {
+	pathPattern string
+	method      string
+}
+
 // EmptyCallOption does not alter the Call configuration.
 // It can be embedded in another structure to carry satellite data for use
 // by interceptors.
@@ -20,28 +25,23 @@ type EmptyCallOption struct{}
 func (EmptyCallOption) before(*callInfo) error      { return nil }
 func (EmptyCallOption) after(*callInfo, *csAttempt) {}
 
-type callInfo struct {
-	bodyPattern string
-	method      string
-}
-
 type csAttempt struct{}
 
-// BodyPattern is bodyPattern
-func BodyPattern(bodyPattern string) CallOption {
-	return BodyPatternCallOption{BodyPattern: bodyPattern}
+// PathPattern is pathpattern
+func PathPattern(pathPattern string) CallOption {
+	return PathPatternCallOption{PathPattern: pathPattern}
 }
 
-// BodyPatternCallOption is BodyPattern
-type BodyPatternCallOption struct {
-	BodyPattern string
+// PathPatternCallOption is BodyPattern
+type PathPatternCallOption struct {
+	EmptyCallOption
+	PathPattern string
 }
 
-func (o BodyPatternCallOption) before(c *callInfo) error {
-	c.bodyPattern = o.BodyPattern
+func (o PathPatternCallOption) before(c *callInfo) error {
+	c.pathPattern = o.PathPattern
 	return nil
 }
-func (o BodyPatternCallOption) after(c *callInfo, attempt *csAttempt) {}
 
 // Method is Method
 func Method(method string) CallOption {
@@ -50,6 +50,7 @@ func Method(method string) CallOption {
 
 // MethodCallOption is BodyCallOption
 type MethodCallOption struct {
+	EmptyCallOption
 	Method string
 }
 
@@ -57,11 +58,9 @@ func (o MethodCallOption) before(c *callInfo) error {
 	c.method = o.Method
 	return nil
 }
-func (o MethodCallOption) after(c *callInfo, attempt *csAttempt) {}
 
 func defaultCallInfo() callInfo {
 	return callInfo{
-		bodyPattern: "*",
-		method:      "POST",
+		method: "POST",
 	}
 }
diff --git a/transport/http/client.go b/transport/http/client.go
index 80525690..3ef20e58 100644
--- a/transport/http/client.go
+++ b/transport/http/client.go
@@ -14,8 +14,6 @@ import (
 	"github.com/go-kratos/kratos/v2/internal/httputil"
 	"github.com/go-kratos/kratos/v2/middleware"
 	"github.com/go-kratos/kratos/v2/transport"
-	"github.com/go-kratos/kratos/v2/transport/http/binding"
-	"google.golang.org/protobuf/proto"
 )
 
 // Client is http client
@@ -147,7 +145,7 @@ func NewClient(ctx context.Context, opts ...ClientOption) (*Client, error) {
 }
 
 // Invoke makes an rpc call procedure for remote service.
-func (client *Client) Invoke(ctx context.Context, pathPattern string, args interface{}, reply interface{}, opts ...CallOption) error {
+func (client *Client) Invoke(ctx context.Context, path string, args interface{}, reply interface{}, opts ...CallOption) error {
 	var (
 		reqBody     io.Reader
 		contentType string
@@ -160,14 +158,7 @@ func (client *Client) Invoke(ctx context.Context, pathPattern string, args inter
 		}
 	}
 
-	path := pathPattern
 	if args != nil {
-		// TODO: support for struct path bindings
-		path = binding.ProtoPath(path, args.(proto.Message))
-	}
-	url := fmt.Sprintf("%s://%s%s", client.schema, client.endpoint, path)
-	if args != nil && c.bodyPattern != "" {
-		// TODO: only encode the target field of args
 		var (
 			body []byte
 			err  error
@@ -178,20 +169,21 @@ func (client *Client) Invoke(ctx context.Context, pathPattern string, args inter
 		}
 		reqBody = bytes.NewReader(body)
 	}
+	url := fmt.Sprintf("%s://%s%s", client.schema, client.endpoint, path)
 	req, err := http.NewRequest(c.method, url, reqBody)
 	if err != nil {
 		return err
 	}
-	if client.userAgent != "" {
-		req.Header.Set("User-Agent", client.userAgent)
-	}
 	if contentType != "" {
 		req.Header.Set("Content-Type", contentType)
 	}
+	if client.userAgent != "" {
+		req.Header.Set("User-Agent", client.userAgent)
+	}
 
 	ctx = transport.NewContext(ctx, transport.Transport{Kind: transport.KindHTTP})
 	ctx = NewClientContext(ctx, ClientInfo{
-		PathPattern: pathPattern,
+		PathPattern: c.pathPattern,
 		Request:     req,
 	})
 
diff --git a/transport/http/handle.go b/transport/http/handle.go
index e843240f..21972edd 100644
--- a/transport/http/handle.go
+++ b/transport/http/handle.go
@@ -13,7 +13,6 @@ import (
 	"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"
 )
 
 // SupportPackageIsVersion1 These constants should not be referenced from any other code.
@@ -167,9 +166,6 @@ func defaultRequestDecoder(req *http.Request, v interface{}) error {
 			return errors.BadRequest("CODEC", err.Error())
 		}
 	}
-	if err := binding.BindVars(mux.Vars(req), v); err != nil {
-		return errors.BadRequest("CODEC", err.Error())
-	}
 	return nil
 }
 


Test patch:
diff --git a/examples/proto/echo_service_test.go b/examples/proto/echo_service_test.go
index e6ccfbae..555867fb 100644
--- a/examples/proto/echo_service_test.go
+++ b/examples/proto/echo_service_test.go
@@ -119,17 +119,20 @@ func testEchoClient(t *testing.T, addr string) {
 		}}
 		dout *DynamicMessageUpdate
 	)
-	if dout, err = cli.EchoPatch(context.Background(), din); err != nil {
+	if dout, err = cli.EchoResponseBody(context.Background(), din); err != nil {
 		t.Fatal(err)
 	}
 	if din.Body.ValueField.GetStringValue() != dout.Body.ValueField.GetStringValue() {
-		t.Errorf("EchoPatch expected %s got %s", din, dout)
+		t.Fatalf("EchoResponseBody expected %s got %s", din, dout)
 	}
-	if dout, err = cli.EchoResponseBody(context.Background(), din); err != nil {
+	if dout, err = cli.EchoPatch(context.Background(), din); err != nil {
 		t.Fatal(err)
 	}
+	if dout.Body == nil {
+		panic("dout.body is nil")
+	}
 	if din.Body.ValueField.GetStringValue() != dout.Body.ValueField.GetStringValue() {
-		t.Errorf("EchoResponseBody expected %s got %s", din, dout)
+		t.Fatalf("EchoPatch expected %s got %s", din, dout)
 	}
 }
 
diff --git a/transport/http/binding/proto_test.go b/transport/http/binding/proto_test.go
index 944d0214..6a828b23 100644
--- a/transport/http/binding/proto_test.go
+++ b/transport/http/binding/proto_test.go
@@ -6,25 +6,25 @@ import (
 )
 
 func TestProtoPath(t *testing.T) {
-	url := ProtoPath("http://helloworld.Greeter/helloworld/{name}/sub/{sub.name}", &HelloRequest{Name: "test", Sub: &Sub{Name: "2233!!!"}})
+	url := EncodePath("POST", "http://helloworld.Greeter/helloworld/{name}/sub/{sub.name}", &HelloRequest{Name: "test", Sub: &Sub{Name: "2233!!!"}})
 	fmt.Println(url)
 	if url != `http://helloworld.Greeter/helloworld/test/sub/2233!!!` {
 		t.Fatalf("proto path not expected!actual: %s ", url)
 	}
 
-	url = ProtoPath("http://helloworld.Greeter/helloworld/sub", &HelloRequest{Name: "test", Sub: &Sub{Name: "2233!!!"}})
+	url = EncodePath("POST", "http://helloworld.Greeter/helloworld/sub", &HelloRequest{Name: "test", Sub: &Sub{Name: "2233!!!"}})
 	fmt.Println(url)
 	if url != `http://helloworld.Greeter/helloworld/sub` {
 		t.Fatalf("proto path not expected!actual: %s ", url)
 	}
 
-	url = ProtoPath("http://helloworld.Greeter/helloworld/{name}/sub/{sub.name}", &HelloRequest{Name: "test"})
+	url = EncodePath("POST", "http://helloworld.Greeter/helloworld/{name}/sub/{sub.name}", &HelloRequest{Name: "test"})
 	fmt.Println(url)
 	if url != `http://helloworld.Greeter/helloworld/test/sub/` {
 		t.Fatalf("proto path not expected!actual: %s ", url)
 	}
 
-	url = ProtoPath("http://helloworld.Greeter/helloworld/{name}/sub/{sub.name33}", &HelloRequest{Name: "test"})
+	url = EncodePath("POST", "http://helloworld.Greeter/helloworld/{name}/sub/{sub.name33}", &HelloRequest{Name: "test"})
 	fmt.Println(url)
 	if url != `http://helloworld.Greeter/helloworld/test/sub/{sub.name33}` {
 		t.Fatalf("proto path not expected!actual: %s ", url)