Repository: cloudwego/hertz
Pull request #854 (2023-07-12 11:06:09)

feat(hz): add more template parameter
#### What type of PR is this?
<!--
Add one of the following kinds:

build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
docs: Documentation only changes
feat: A new feature
optimize: A new optimization
fix: A bug fix
perf: A code change that improves performance
refactor: A code change that neither fixes a bug nor adds a feature
style: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc)
test: Adding missing tests or correcting existing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
-->
feat
#### Check the PR title.
<!--
The description of the title will be attached in Release Notes, 
so please describe it from user-oriented, what this PR does / why we need it.
Please check your PR title with the below requirements:
-->
- [x] This PR title match the format: `<type>(optional scope): <description>`.
- [x] The description of this PR title is user-oriented and clear enough for others to understand.
- [x] Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. [User docs repo](https://github.com/cloudwego/cloudwego.github.io).

#### (Optional) Translate the PR title into Chinese.
添加更多的模版参数

#### (Optional) More detail description for this PR(en: English/zh: Chinese).
<!--
Provide more detailed info for review. If it is a perf type PR, perf data is suggested to give.
-->
en: Add more template parameter
zh(optional): 添加更多的模版参数

#### (Optional) Which issue(s) this PR fixes:
<!--
Automatically closes linked issue when PR is merged.
Eg: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->

#### (Optional) The PR that updates user documentation:
<!--
If the current PR requires user awareness at the usage level, please submit a PR to update user docs. [User docs repo](https://github.com/cloudwego/cloudwego.github.io)
-->



Patch:
diff --git a/_typos.toml b/_typos.toml
index 9808229..3d3103e 100644
--- a/_typos.toml
+++ b/_typos.toml
@@ -17,4 +17,5 @@ referer = "referer"
 HeaderReferer = "HeaderReferer"
 expectedReferer = "expectedReferer"
 Referer = "Referer"
-O_WRONLY = "O_WRONLY"
\ No newline at end of file
+O_WRONLY = "O_WRONLY"
+WRONLY = "WRONLY"
\ No newline at end of file
diff --git a/cmd/hz/app/app.go b/cmd/hz/app/app.go
index 239b4df..269e5ba 100644
--- a/cmd/hz/app/app.go
+++ b/cmd/hz/app/app.go
@@ -184,6 +184,7 @@ func Init() *cli.App {
 	unsetOmitemptyFlag := cli.BoolFlag{Name: "unset_omitempty", Usage: "Remove 'omitempty' tag for generated struct.", Destination: &globalArgs.UnsetOmitempty}
 	protoCamelJSONTag := cli.BoolFlag{Name: "pb_camel_json_tag", Usage: "Convert Name style for json tag to camel(Only works protobuf).", Destination: &globalArgs.ProtobufCamelJSONTag}
 	snakeNameFlag := cli.BoolFlag{Name: "snake_tag", Usage: "Use snake_case style naming for tags. (Only works for 'form', 'query', 'json')", Destination: &globalArgs.SnakeName}
+	rmTagFlag := cli.StringSliceFlag{Name: "rm_tag", Usage: "Remove the specified tag"}
 	customLayout := cli.StringFlag{Name: "customize_layout", Usage: "Specify the path for layout template.", Destination: &globalArgs.CustomizeLayout}
 	customLayoutData := cli.StringFlag{Name: "customize_layout_data_path", Usage: "Specify the path for layout template render data.", Destination: &globalArgs.CustomizeLayoutData}
 	customPackage := cli.StringFlag{Name: "customize_package", Usage: "Specify the path for package template.", Destination: &globalArgs.CustomizePackage}
@@ -230,6 +231,7 @@ func Init() *cli.App {
 				&unsetOmitemptyFlag,
 				&protoCamelJSONTag,
 				&snakeNameFlag,
+				&rmTagFlag,
 				&excludeFilesFlag,
 				&customLayout,
 				&customLayoutData,
@@ -263,6 +265,7 @@ func Init() *cli.App {
 				&unsetOmitemptyFlag,
 				&protoCamelJSONTag,
 				&snakeNameFlag,
+				&rmTagFlag,
 				&excludeFilesFlag,
 				&customPackage,
 				&handlerByMethod,
@@ -289,6 +292,7 @@ func Init() *cli.App {
 				&unsetOmitemptyFlag,
 				&protoCamelJSONTag,
 				&snakeNameFlag,
+				&rmTagFlag,
 				&excludeFilesFlag,
 			},
 			Action: Model,
@@ -315,6 +319,7 @@ func Init() *cli.App {
 				&unsetOmitemptyFlag,
 				&protoCamelJSONTag,
 				&snakeNameFlag,
+				&rmTagFlag,
 				&excludeFilesFlag,
 				&customLayout,
 				&customLayoutData,
diff --git a/cmd/hz/config/argument.go b/cmd/hz/config/argument.go
index 8eef041..b8bf41c 100644
--- a/cmd/hz/config/argument.go
+++ b/cmd/hz/config/argument.go
@@ -64,6 +64,7 @@ type Argument struct {
 	ProtobufPlugins      []string
 	ThriftPlugins        []string
 	SnakeName            bool
+	RmTags               []string
 	Excludes             []string
 	NoRecurse            bool
 	HandlerByMethod      bool
@@ -121,6 +122,7 @@ func (arg *Argument) parseStringSlice(c *cli.Context) {
 	arg.ProtocOptions = c.StringSlice("protoc")
 	arg.ThriftPlugins = c.StringSlice("thrift-plugins")
 	arg.ProtobufPlugins = c.StringSlice("protoc-plugins")
+	arg.RmTags = c.StringSlice("rm_tag")
 }
 
 func (arg *Argument) UpdateByManifest(m *meta.Manifest) {
diff --git a/cmd/hz/generator/custom_files.go b/cmd/hz/generator/custom_files.go
index 390a40d..1efaae0 100644
--- a/cmd/hz/generator/custom_files.go
+++ b/cmd/hz/generator/custom_files.go
@@ -48,16 +48,17 @@ type IDLPackageRenderInfo struct {
 
 type CustomizedFileForMethod struct {
 	*HttpMethod
-	FilePath    string
-	FilePackage string
-	ServiceInfo *Service // service info for that method
+	FilePath       string
+	FilePackage    string
+	ServiceInfo    *Service              // service info for this method
+	IDLPackageInfo *IDLPackageRenderInfo // IDL info for this service
 }
 
 type CustomizedFileForService struct {
 	*Service
 	FilePath       string
 	FilePackage    string
-	IDLPackageInfo *IDLPackageRenderInfo // IDL info for that service
+	IDLPackageInfo *IDLPackageRenderInfo // IDL info for this service
 }
 
 type CustomizedFileForIDL struct {
@@ -110,7 +111,7 @@ func (pkgGen *HttpPackageGenerator) genCustomizedFile(pkg *HttpPackage) error {
 						filePathRenderInfo.ServiceName = service.Name
 						filePathRenderInfo.MethodName = method.Name
 						filePathRenderInfo.HandlerGenPath = method.OutputDir
-						err := pkgGen.genLoopMethod(tplInfo, filePathRenderInfo, method, service)
+						err := pkgGen.genLoopMethod(tplInfo, filePathRenderInfo, method, service, &idlPackageRenderInfo)
 						if err != nil {
 							return err
 						}
@@ -323,10 +324,11 @@ func (pkgGen *HttpPackageGenerator) genLoopService(tplInfo *Template, filePathRe
 			if tplInfo.UpdateBehavior.AppendKey == "method" {
 				for _, method := range service.Methods {
 					data := CustomizedFileForMethod{
-						HttpMethod:  method,
-						FilePath:    filePath,
-						FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
-						ServiceInfo: service,
+						HttpMethod:     method,
+						FilePath:       filePath,
+						FilePackage:    util.SplitPackage(filepath.Dir(filePath), ""),
+						ServiceInfo:    service,
+						IDLPackageInfo: idlPackageRenderInfo,
 					}
 					insertKey, err := renderInsertKey(tplInfo, data)
 					if err != nil {
@@ -395,7 +397,7 @@ func (pkgGen *HttpPackageGenerator) genLoopService(tplInfo *Template, filePathRe
 }
 
 // genLoopMethod used to generate files by 'method'
-func (pkgGen *HttpPackageGenerator) genLoopMethod(tplInfo *Template, filePathRenderInfo FilePathRenderInfo, method *HttpMethod, service *Service) error {
+func (pkgGen *HttpPackageGenerator) genLoopMethod(tplInfo *Template, filePathRenderInfo FilePathRenderInfo, method *HttpMethod, service *Service, idlPackageRenderInfo *IDLPackageRenderInfo) error {
 	filePath, err := renderFilePath(tplInfo, filePathRenderInfo)
 	if err != nil {
 		return err
@@ -408,10 +410,11 @@ func (pkgGen *HttpPackageGenerator) genLoopMethod(tplInfo *Template, filePathRen
 
 	if !exist { // create file
 		data := CustomizedFileForMethod{
-			HttpMethod:  method,
-			FilePath:    filePath,
-			FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
-			ServiceInfo: service,
+			HttpMethod:     method,
+			FilePath:       filePath,
+			FilePackage:    util.SplitPackage(filepath.Dir(filePath), ""),
+			ServiceInfo:    service,
+			IDLPackageInfo: idlPackageRenderInfo,
 		}
 		err := pkgGen.TemplateGenerator.Generate(data, tplInfo.Path, filePath, false)
 		if err != nil {
@@ -426,10 +429,11 @@ func (pkgGen *HttpPackageGenerator) genLoopMethod(tplInfo *Template, filePathRen
 			// re-generate
 			logs.Infof("re-generate file '%s', because the update behavior is 'Regenerate'", filePath)
 			data := CustomizedFileForMethod{
-				HttpMethod:  method,
-				FilePath:    filePath,
-				FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
-				ServiceInfo: service,
+				HttpMethod:     method,
+				FilePath:       filePath,
+				FilePackage:    util.SplitPackage(filepath.Dir(filePath), ""),
+				ServiceInfo:    service,
+				IDLPackageInfo: idlPackageRenderInfo,
 			}
 			err := pkgGen.TemplateGenerator.Generate(data, tplInfo.Path, filePath, false)
 			if err != nil {
@@ -497,10 +501,11 @@ func (pkgGen *HttpPackageGenerator) genSingleCustomizedFile(tplInfo *Template, f
 				for _, service := range idlPackageRenderInfo.ServiceInfos.Services {
 					for _, method := range service.Methods {
 						data := CustomizedFileForMethod{
-							HttpMethod:  method,
-							FilePath:    filePath,
-							FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
-							ServiceInfo: service,
+							HttpMethod:     method,
+							FilePath:       filePath,
+							FilePackage:    util.SplitPackage(filepath.Dir(filePath), ""),
+							ServiceInfo:    service,
+							IDLPackageInfo: &idlPackageRenderInfo,
 						}
 						insertKey, err := renderInsertKey(tplInfo, data)
 						if err != nil {
diff --git a/cmd/hz/generator/handler.go b/cmd/hz/generator/handler.go
index cecb0d0..4d797d2 100644
--- a/cmd/hz/generator/handler.go
+++ b/cmd/hz/generator/handler.go
@@ -29,17 +29,21 @@ import (
 )
 
 type HttpMethod struct {
-	Name            string
-	HTTPMethod      string
-	Comment         string
-	RequestTypeName string
-	ReturnTypeName  string
-	Path            string
-	Serializer      string
-	OutputDir       string
-	RefPackage      string
-	RefPackageAlias string
-	ModelPackage    map[string]string
+	Name               string
+	HTTPMethod         string
+	Comment            string
+	RequestTypeName    string
+	RequestTypePackage string
+	RequestTypeRawName string
+	ReturnTypeName     string
+	ReturnTypePackage  string
+	ReturnTypeRawName  string
+	Path               string
+	Serializer         string
+	OutputDir          string
+	RefPackage         string // handler import dir
+	RefPackageAlias    string // handler import alias
+	ModelPackage       map[string]string
 	// Annotations     map[string]string
 	Models map[string]*model.Model
 }
diff --git a/cmd/hz/generator/layout.go b/cmd/hz/generator/layout.go
index 3791955..d007fa8 100644
--- a/cmd/hz/generator/layout.go
+++ b/cmd/hz/generator/layout.go
@@ -25,6 +25,7 @@ import (
 	"reflect"
 	"strings"
 
+	"github.com/cloudwego/hertz/cmd/hz/meta"
 	"github.com/cloudwego/hertz/cmd/hz/util"
 	"gopkg.in/yaml.v2"
 )
@@ -169,10 +170,14 @@ func serviceToLayoutData(service Layout) (map[string]interface{}, error) {
 	if len(service.RouterDir) != 0 {
 		routerPkg = filepath.Base(service.RouterDir)
 	}
+	serviceName := service.ServiceName
+	if len(serviceName) == 0 {
+		serviceName = meta.DefaultServiceName
+	}
 
 	return map[string]interface{}{
 		"GoModule":        goMod,
-		"ServiceName":     service.ServiceName,
+		"ServiceName":     serviceName,
 		"UseApacheThrift": service.UseApacheThrift,
 		"HandlerPkg":      handlerPkg,
 		"RouterPkg":       routerPkg,
diff --git a/cmd/hz/meta/const.go b/cmd/hz/meta/const.go
index 6ec98d6..66f0a1f 100644
--- a/cmd/hz/meta/const.go
+++ b/cmd/hz/meta/const.go
@@ -21,6 +21,8 @@ import "runtime"
 // Version hz version
 const Version = "v0.6.5"
 
+const DefaultServiceName = "hertz_service"
+
 // Mode hz run modes
 type Mode int
 
diff --git a/cmd/hz/protobuf/api/api.proto b/cmd/hz/protobuf/api/api.proto
index a41e2f8..72edf41 100644
--- a/cmd/hz/protobuf/api/api.proto
+++ b/cmd/hz/protobuf/api/api.proto
@@ -24,6 +24,8 @@ extend google.protobuf.FieldOptions {
   optional string js_conv_compatible = 50132;
   optional string file_name_compatible = 50133;
   optional string none_compatible = 50134;
+  // 50135 is reserved to vt_compatible
+  // optional FieldRules vt_compatible = 50135;
 
   optional string go_tag = 51001;
 }
@@ -62,4 +64,12 @@ extend google.protobuf.ServiceOptions {
 
   // 50731~50760 used to extend service option by hz
   optional string base_domain_compatible = 50731;
+}
+
+extend google.protobuf.MessageOptions {
+  // optional FieldRules msg_vt = 50111;
+
+  optional string reserve = 50830;
+  // 550831 is reserved to msg_vt_compatible
+  // optional FieldRules msg_vt_compatible = 50831;
 }
\ No newline at end of file
diff --git a/cmd/hz/protobuf/ast.go b/cmd/hz/protobuf/ast.go
index 2abe833..fcb8197 100644
--- a/cmd/hz/protobuf/ast.go
+++ b/cmd/hz/protobuf/ast.go
@@ -158,16 +158,20 @@ func astToService(ast *descriptorpb.FileDescriptorProto, resolver *Resolver, cmd
 
 			reqName := m.GetInputType()
 			sb, err := resolver.ResolveIdentifier(reqName)
-			reqName = util.BaseName(sb.Scope.GetOptions().GetGoPackage(), "") + "." + inputGoType.GoIdent.GoName
 			if err != nil {
 				return nil, err
 			}
+			reqName = util.BaseName(sb.Scope.GetOptions().GetGoPackage(), "") + "." + inputGoType.GoIdent.GoName
+			reqRawName := inputGoType.GoIdent.GoName
+			reqPackage := util.BaseName(sb.Scope.GetOptions().GetGoPackage(), "")
 			respName := m.GetOutputType()
 			st, err := resolver.ResolveIdentifier(respName)
-			respName = util.BaseName(st.Scope.GetOptions().GetGoPackage(), "") + "." + outputGoType.GoIdent.GoName
 			if err != nil {
 				return nil, err
 			}
+			respName = util.BaseName(st.Scope.GetOptions().GetGoPackage(), "") + "." + outputGoType.GoIdent.GoName
+			respRawName := outputGoType.GoIdent.GoName
+			respPackage := util.BaseName(sb.Scope.GetOptions().GetGoPackage(), "")
 
 			var serializer string
 			sl, sv := checkFirstOptions(SerializerOptions, m.GetOptions())
@@ -212,7 +216,11 @@ func astToService(ast *descriptorpb.FileDescriptorProto, resolver *Resolver, cmd
 				respName = goOptMapAlias[st.Scope.GetOptions().GetGoPackage()] + "." + outputGoType.GoIdent.GoName
 			}
 			method.RequestTypeName = reqName
+			method.RequestTypeRawName = reqRawName
+			method.RequestTypePackage = reqPackage
 			method.ReturnTypeName = respName
+			method.ReturnTypeRawName = respRawName
+			method.ReturnTypePackage = respPackage
 
 			methods = append(methods, method)
 
@@ -269,14 +277,14 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, gen *protogen
 		if proto.HasExtension(f.Desc.Options(), api.E_Query) {
 			hasAnnotation = true
 			queryAnnos := proto.GetExtension(f.Desc.Options(), api.E_Query)
-			val := queryAnnos.(string)
+			val := checkSnakeName(queryAnnos.(string))
 			clientMethod.QueryParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", val, f.GoName)
 		}
 
 		if proto.HasExtension(f.Desc.Options(), api.E_Path) {
 			hasAnnotation = true
 			pathAnnos := proto.GetExtension(f.Desc.Options(), api.E_Path)
-			val := pathAnnos.(string)
+			val := checkSnakeName(pathAnnos.(string))
 			if isStringFieldType {
 				clientMethod.PathParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", val, f.GoName)
 			} else {
@@ -287,7 +295,7 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, gen *protogen
 		if proto.HasExtension(f.Desc.Options(), api.E_Header) {
 			hasAnnotation = true
 			headerAnnos := proto.GetExtension(f.Desc.Options(), api.E_Header)
-			val := headerAnnos.(string)
+			val := checkSnakeName(headerAnnos.(string))
 			if isStringFieldType {
 				clientMethod.HeaderParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", val, f.GoName)
 			} else {
@@ -298,7 +306,7 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, gen *protogen
 		if formAnnos := getCompatibleAnnotation(f.Desc.Options(), api.E_Form, api.E_FormCompatible); formAnnos != nil {
 			hasAnnotation = true
 			hasFormAnnotation = true
-			val := formAnnos.(string)
+			val := checkSnakeName(formAnnos.(string))
 			if isStringFieldType {
 				clientMethod.FormValueCode += fmt.Sprintf("%q: req.Get%s(),\n", val, f.GoName)
 			} else {
@@ -314,11 +322,11 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, gen *protogen
 		if fileAnnos := getCompatibleAnnotation(f.Desc.Options(), api.E_FileName, api.E_FileNameCompatible); fileAnnos != nil {
 			hasAnnotation = true
 			hasFormAnnotation = true
-			val := fileAnnos.(string)
+			val := checkSnakeName(fileAnnos.(string))
 			clientMethod.FormFileCode += fmt.Sprintf("%q: req.Get%s(),\n", val, f.GoName)
 		}
 		if !hasAnnotation && strings.EqualFold(clientMethod.HTTPMethod, "get") {
-			clientMethod.QueryParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", f.GoName, f.GoName)
+			clientMethod.QueryParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", checkSnakeName(f.GoName), f.GoName)
 		}
 	}
 	clientMethod.BodyParamsCode = meta.SetBodyParam
diff --git a/cmd/hz/protobuf/plugin.go b/cmd/hz/protobuf/plugin.go
index 82b01cd..e258038 100644
--- a/cmd/hz/protobuf/plugin.go
+++ b/cmd/hz/protobuf/plugin.go
@@ -76,10 +76,22 @@ type Plugin struct {
 	ModelDir     string
 	UseDir       string
 	IdlClientDir string
+	RmTags       RemoveTags
 	PkgMap       map[string]string
 	logger       *logs.StdLogger
 }
 
+type RemoveTags []string
+
+func (rm *RemoveTags) Exist(tag string) bool {
+	for _, rmTag := range *rm {
+		if rmTag == tag {
+			return true
+		}
+	}
+	return false
+}
+
 func (plugin *Plugin) Run() int {
 	plugin.setLogger()
 	args := &config.Argument{}
@@ -192,6 +204,7 @@ func (plugin *Plugin) Handle(req *pluginpb.CodeGeneratorRequest, args *config.Ar
 	opts := protogen.Options{}
 	gen, err := opts.New(req)
 	plugin.Plugin = gen
+	plugin.RmTags = args.RmTags
 	if err != nil {
 		return fmt.Errorf("new protoc plugin failed: %s", err.Error())
 	}
@@ -322,7 +335,6 @@ func (plugin *Plugin) fixModelPathAndPackage(pkg string) (impt, path string) {
 func (plugin *Plugin) GenerateFiles(pluginPb *protogen.Plugin) error {
 	idl := pluginPb.Request.FileToGenerate[len(pluginPb.Request.FileToGenerate)-1]
 	pluginPb.SupportedFeatures = gengo.SupportedFeatures
-
 	for _, f := range pluginPb.Files {
 		if f.Proto.GetName() == idl {
 			err := plugin.GenerateFile(pluginPb, f)
@@ -358,7 +370,7 @@ func (plugin *Plugin) GenerateFile(gen *protogen.Plugin, f *protogen.File) error
 	if len(plugin.UseDir) != 0 {
 		return nil
 	}
-	file, err := generateFile(gen, f)
+	file, err := generateFile(gen, f, plugin.RmTags)
 	if err != nil || file == nil {
 		return fmt.Errorf("generate file %s failed: %s", f.Proto.GetName(), err.Error())
 	}
@@ -366,7 +378,7 @@ func (plugin *Plugin) GenerateFile(gen *protogen.Plugin, f *protogen.File) error
 }
 
 // generateFile generates the contents of a .pb.go file.
-func generateFile(gen *protogen.Plugin, file *protogen.File) (*protogen.GeneratedFile, error) {
+func generateFile(gen *protogen.Plugin, file *protogen.File, rmTags RemoveTags) (*protogen.GeneratedFile, error) {
 	filename := file.GeneratedFilenamePrefix + ".pb.go"
 	g := gen.NewGeneratedFile(filename, file.GoImportPath)
 	f := newFileInfo(file)
@@ -398,7 +410,7 @@ func generateFile(gen *protogen.Plugin, file *protogen.File) (*protogen.Generate
 	}
 	var err error
 	for _, message := range f.allMessages {
-		err = genMessage(g, f, message)
+		err = genMessage(g, f, message, rmTags)
 		if err != nil {
 			return nil, err
 		}
@@ -410,7 +422,7 @@ func generateFile(gen *protogen.Plugin, file *protogen.File) (*protogen.Generate
 	return g, nil
 }
 
-func genMessage(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) error {
+func genMessage(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, rmTags RemoveTags) error {
 	if m.Desc.IsMapEntry() {
 		return nil
 	}
@@ -421,7 +433,7 @@ func genMessage(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) error {
 		m.Desc.Options().(*descriptorpb.MessageOptions).GetDeprecated())
 	g.P(leadingComments,
 		"type ", m.GoIdent, " struct {")
-	err := genMessageFields(g, f, m)
+	err := genMessageFields(g, f, m, rmTags)
 	if err != nil {
 		return err
 	}
@@ -435,12 +447,12 @@ func genMessage(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) error {
 	return nil
 }
 
-func genMessageFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) error {
+func genMessageFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, rmTags RemoveTags) error {
 	sf := f.allMessageFieldsByPtr[m]
 	genMessageInternalFields(g, f, m, sf)
 	var err error
 	for _, field := range m.Fields {
-		err = genMessageField(g, f, m, field, sf)
+		err = genMessageField(g, f, m, field, sf, rmTags)
 		if err != nil {
 			return err
 		}
@@ -448,7 +460,7 @@ func genMessageFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) er
 	return nil
 }
 
-func genMessageField(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field, sf *structFields) error {
+func genMessageField(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field, sf *structFields, rmTags RemoveTags) error {
 	if oneof := field.Oneof; oneof != nil && !oneof.Desc.IsSynthetic() {
 		// It would be a bit simpler to iterate over the oneofs below,
 		// but generating the field here keeps the contents of the Go
@@ -506,6 +518,16 @@ func genMessageField(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, fie
 		tags = append(tags, gotrackTags...)
 	}
 
+	if len(rmTags) > 0 {
+		tmp := structTags{}
+		for _, tag := range tags {
+			if !rmTags.Exist(tag[0]) {
+				tmp = append(tmp, tag)
+			}
+		}
+		tags = tmp
+	}
+
 	name := field.GoName
 	if field.Desc.IsWeak() {
 		name = WeakFieldPrefix_goname + name
diff --git a/cmd/hz/thrift/ast.go b/cmd/hz/thrift/ast.go
index 298bad0..557b50a 100644
--- a/cmd/hz/thrift/ast.go
+++ b/cmd/hz/thrift/ast.go
@@ -122,7 +122,7 @@ func astToService(ast *parser.Thrift, resolver *Resolver, args *config.Argument)
 				return nil, fmt.Errorf("invalid api.%s  for %s.%s: %s", hmethod, s.Name, m.Name, path)
 			}
 
-			var reqName string
+			var reqName, reqRawName, reqPackage string
 			if len(m.Arguments) >= 1 {
 				if len(m.Arguments) > 1 {
 					logs.Warnf("function '%s' has more than one argument, but only the first can be used in hertz now", m.GetName())
@@ -132,25 +132,49 @@ func astToService(ast *parser.Thrift, resolver *Resolver, args *config.Argument)
 				if err != nil {
 					return nil, err
 				}
+				if strings.Contains(reqName, ".") && !m.Arguments[0].GetType().Category.IsContainerType() {
+					// If reqName contains "." , then it must be of the form "pkg.name".
+					// so reqRawName='name', reqPackage='pkg'
+					names := strings.Split(reqName, ".")
+					if len(names) != 2 {
+						return nil, fmt.Errorf("request name: %s is wrong", reqName)
+					}
+					reqRawName = names[1]
+					reqPackage = names[0]
+				}
 			}
-			var respName string
+			var respName, respRawName, respPackage string
 			if !m.Oneway {
 				var err error
 				respName, err = resolver.ResolveTypeName(m.GetFunctionType())
 				if err != nil {
 					return nil, err
 				}
+				if strings.Contains(respName, ".") && !m.GetFunctionType().Category.IsContainerType() {
+					names := strings.Split(respName, ".")
+					if len(names) != 2 {
+						return nil, fmt.Errorf("response name: %s is wrong", respName)
+					}
+					// If respName contains "." , then it must be of the form "pkg.name".
+					// so respRawName='name', respPackage='pkg'
+					respRawName = names[1]
+					respPackage = names[0]
+				}
 			}
 
 			sr, _ := util.GetFirstKV(getAnnotations(m.Annotations, SerializerTags))
 			method := &generator.HttpMethod{
-				Name:            util.CamelString(m.GetName()),
-				HTTPMethod:      hmethod,
-				RequestTypeName: reqName,
-				ReturnTypeName:  respName,
-				Path:            path[0],
-				Serializer:      sr,
-				OutputDir:       handlerOutDir,
+				Name:               util.CamelString(m.GetName()),
+				HTTPMethod:         hmethod,
+				RequestTypeName:    reqName,
+				RequestTypeRawName: reqRawName,
+				RequestTypePackage: reqPackage,
+				ReturnTypeName:     respName,
+				ReturnTypeRawName:  respRawName,
+				ReturnTypePackage:  respPackage,
+				Path:               path[0],
+				Serializer:         sr,
+				OutputDir:          handlerOutDir,
 				// Annotations:     m.Annotations,
 			}
 			refs := resolver.ExportReferred(false, true)
@@ -217,13 +241,13 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, p *parser.Typ
 		}
 		if anno := getAnnotation(field.Annotations, AnnotationQuery); len(anno) > 0 {
 			hasAnnotation = true
-			query := anno[0]
+			query := checkSnakeName(anno[0])
 			clientMethod.QueryParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", query, field.GoName().String())
 		}
 
 		if anno := getAnnotation(field.Annotations, AnnotationPath); len(anno) > 0 {
 			hasAnnotation = true
-			path := anno[0]
+			path := checkSnakeName(anno[0])
 			if isStringFieldType {
 				clientMethod.PathParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", path, field.GoName().String())
 			} else {
@@ -233,7 +257,7 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, p *parser.Typ
 
 		if anno := getAnnotation(field.Annotations, AnnotationHeader); len(anno) > 0 {
 			hasAnnotation = true
-			header := anno[0]
+			header := checkSnakeName(anno[0])
 			if isStringFieldType {
 				clientMethod.HeaderParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", header, field.GoName().String())
 			} else {
@@ -243,7 +267,7 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, p *parser.Typ
 
 		if anno := getAnnotation(field.Annotations, AnnotationForm); len(anno) > 0 {
 			hasAnnotation = true
-			form := anno[0]
+			form := checkSnakeName(anno[0])
 			hasFormAnnotation = true
 			if isStringFieldType {
 				clientMethod.FormValueCode += fmt.Sprintf("%q: req.Get%s(),\n", form, field.GoName().String())
@@ -259,12 +283,12 @@ func parseAnnotationToClient(clientMethod *generator.ClientMethod, p *parser.Typ
 
 		if anno := getAnnotation(field.Annotations, AnnotationFileName); len(anno) > 0 {
 			hasAnnotation = true
-			fileName := anno[0]
+			fileName := checkSnakeName(anno[0])
 			hasFormAnnotation = true
 			clientMethod.FormFileCode += fmt.Sprintf("%q: req.Get%s(),\n", fileName, field.GoName().String())
 		}
 		if !hasAnnotation && strings.EqualFold(clientMethod.HTTPMethod, "get") {
-			clientMethod.QueryParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", field.GoName().String(), field.GoName().String())
+			clientMethod.QueryParamsCode += fmt.Sprintf("%q: req.Get%s(),\n", checkSnakeName(field.GoName().String()), field.GoName().String())
 		}
 	}
 	clientMethod.BodyParamsCode = meta.SetBodyParam
diff --git a/cmd/hz/thrift/plugin.go b/cmd/hz/thrift/plugin.go
index 23c5c75..ff4fdf6 100644
--- a/cmd/hz/thrift/plugin.go
+++ b/cmd/hz/thrift/plugin.go
@@ -41,6 +41,7 @@ type Plugin struct {
 	req    *thriftgo_plugin.Request
 	args   *config.Argument
 	logger *logs.StdLogger
+	rmTags []string
 }
 
 func (plugin *Plugin) Run() int {
@@ -74,6 +75,7 @@ func (plugin *Plugin) Run() int {
 		logs.Errorf("parse args failed: %s", err.Error())
 		return meta.PluginError
 	}
+	plugin.rmTags = args.RmTags
 	if args.CmdType == meta.CmdModel {
 		res, err := plugin.GetResponse(nil, args.OutDir)
 		if err != nil {
@@ -334,7 +336,7 @@ func (plugin *Plugin) InsertTag() ([]*thriftgo_plugin.Generated, error) {
 			stName := st.GetName()
 			for _, f := range st.Fields {
 				fieldName := f.GetName()
-				tagString, err := getTagString(f)
+				tagString, err := getTagString(f, plugin.rmTags)
 				if err != nil {
 					return nil, err
 				}
@@ -360,7 +362,7 @@ func (plugin *Plugin) InsertTag() ([]*thriftgo_plugin.Generated, error) {
 			stName := st.GetName()
 			for _, f := range st.Fields {
 				fieldName := f.GetName()
-				tagString, err := getTagString(f)
+				tagString, err := getTagString(f, plugin.rmTags)
 				if err != nil {
 					return nil, err
 				}
@@ -400,7 +402,7 @@ func (plugin *Plugin) GetResponse(files []generator.File, outputDir string) (*th
 	}, nil
 }
 
-func getTagString(f *parser.Field) (string, error) {
+func getTagString(f *parser.Field, rmTags []string) (string, error) {
 	field := model.Field{}
 	err := injectTags(f, &field, true, false)
 	if err != nil {
@@ -412,6 +414,11 @@ func getTagString(f *parser.Field) (string, error) {
 			disableTag = true
 		}
 	}
+
+	for _, rmTag := range rmTags {
+		field.Tags.Remove(rmTag)
+	}
+
 	var tagString string
 	tags := field.Tags
 	for idx, tag := range tags {
diff --git a/pkg/protocol/http1/client.go b/pkg/protocol/http1/client.go
index 13f07a1..34bb5f0 100644
--- a/pkg/protocol/http1/client.go
+++ b/pkg/protocol/http1/client.go
@@ -381,7 +381,8 @@ func (c *HostClient) Do(ctx context.Context, req *protocol.Request, resp *protoc
 	req.Options().StartRequest()
 	for {
 		canIdempotentRetry, err = c.do(req, resp)
-		if err == nil {
+		// If there is no custom retry and err is equal to nil, the loop simply exits.
+		if err == nil && isDefaultRetryFunc {
 			break
 		}
 


Test patch:
diff --git a/pkg/app/client/client_test.go b/pkg/app/client/client_test.go
index 84c6a91..ba00f78 100644
--- a/pkg/app/client/client_test.go
+++ b/pkg/app/client/client_test.go
@@ -2296,3 +2296,54 @@ func TestClientState(t *testing.T) {
 	client.Get(context.Background(), nil, "http://127.0.0.1:11000")
 	time.Sleep(time.Second * 22)
 }
+
+func TestClientRetryErr(t *testing.T) {
+	t.Run("200", func(t *testing.T) {
+		opt := config.NewOptions([]config.Option{})
+		opt.Addr = "127.0.0.1:10136"
+		engine := route.NewEngine(opt)
+		var l sync.Mutex
+		retryNum := 0
+		engine.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
+			l.Lock()
+			defer l.Unlock()
+			retryNum += 1
+			ctx.SetStatusCode(200)
+		})
+		go engine.Run()
+		time.Sleep(100 * time.Millisecond)
+		c, _ := NewClient(WithRetryConfig(retry.WithMaxAttemptTimes(3)))
+		_, _, err := c.Get(context.Background(), nil, "http://127.0.0.1:10136/ping")
+		assert.Nil(t, err)
+		l.Lock()
+		assert.DeepEqual(t, 1, retryNum)
+		l.Unlock()
+		engine.Close()
+	})
+
+	t.Run("502", func(t *testing.T) {
+		opt := config.NewOptions([]config.Option{})
+		opt.Addr = "127.0.0.1:10137"
+		engine := route.NewEngine(opt)
+		var l sync.Mutex
+		retryNum := 0
+		engine.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
+			l.Lock()
+			defer l.Unlock()
+			retryNum += 1
+			ctx.SetStatusCode(502)
+		})
+		go engine.Run()
+		time.Sleep(100 * time.Millisecond)
+		c, _ := NewClient(WithRetryConfig(retry.WithMaxAttemptTimes(3)))
+		c.SetRetryIfFunc(func(req *protocol.Request, resp *protocol.Response, err error) bool {
+			return resp.StatusCode() == 502
+		})
+		_, _, err := c.Get(context.Background(), nil, "http://127.0.0.1:10137/ping")
+		assert.Nil(t, err)
+		l.Lock()
+		assert.DeepEqual(t, 3, retryNum)
+		l.Unlock()
+		engine.Close()
+	})
+}