gRPC服务端模板解析报错unexpected EOF,求修复方案
解析Go接口生成gRPC服务端代码时模板EOF错误排查
问题背景
尝试通过直接解析Go接口消除gRPC服务对proto文件的依赖,已完成接口解析并填充ParsedInterface结构体,但生成服务端代码时触发模板错误:template: server:60: unexpected EOF。
示例接口及结构体:
package main import "gofr.dev/pkg/gofr" // ExampleService Define the service interface with GoFr context type ExampleService interface { GetExampleData(ctx *gofr.Context, request ExampleRequest) (ExampleResponse, error) } // ExampleRequest Define the request struct type ExampleRequest struct { ID int `json:"id"` Name string `json:"name"` } // ExampleResponse Define the response struct type ExampleResponse struct { Status string `json:"status"` Message string `json:"message"` }
出错的服务端模板代码:
package main import ( "bytes" "go/format" "text/template" ) // GenerateCode generates the client and server code func GenerateCode(parsedInterface *ParsedInterface) (string, string, error) { // ... 客户端模板省略 ... serverTemplate := ` package main import ( "context" "fmt" "reflect" "google.golang.org/grpc" ) type {{.InterfaceName}}Server struct { impl interface{} } func New{{.InterfaceName}}Server(impl interface{}) *{{.InterfaceName}}Server { return &{{.InterfaceName}}Server{impl: impl} } {{range .Methods}} func (s *{{$.InterfaceName}}Server) {{.Name}}(ctx context.Context, req struct { {{range .InputParams}}{{.Name}} {{.Type}} {{end}} }) ({{range .ReturnTypes}}{{.Name}} {{.Type}}{{end}}, error) { method := reflect.ValueOf(s.impl).MethodByName("{{.Name}}") if !method.IsValid() { return {{range .ReturnTypes}}{{.Name}}, fmt.Errorf("method {{.Name}} not found") } in := []reflect.Value{reflect.ValueOf(ctx)} {{range .InputParams}}in = append(in, reflect.ValueOf(req.{{.Name}})){{end}} out := method.Call(in) if len(out) != {{len .ReturnTypes}}+1 { return {{range .ReturnTypes}}{{.Name}}, fmt.Errorf("unexpected number of return values") } if err, ok := out[{{len .ReturnTypes}}].Interface().(error); ok && err != nil { return {{range .ReturnTypes}}{{.Name}}, err } resp := struct { {{range .ReturnTypes}} {{.Name}} {{.Type}} {{end}} }{} {{range $i, $type := .ReturnTypes}} {{if $type.Fields}} resp.{{.Name}} = {{.Type}}{ {{range $field := $type.Fields}} {{$field.Name}}: out[{{$i}}].Elem().FieldByName("{{$field.Name}}").Interface().({{$field.Type}}), {{end}} } {{else}} resp.{{.Name}} = out[{{$i}}].Interface().({{$type.Type}}) {{end}} {{end}} return resp, nil } {{end}} func (s *{{.InterfaceName}}Server) Register(grpcServer *grpc.Server) { {{.InterfaceName}}_RegisterService(grpcServer, s.impl) } ` // ... 模板解析及执行代码省略 ... }
错误排查与修复
1. 核心原因:未闭合的模板Range语句
模板中3处return语句使用了{{range .ReturnTypes}}但未添加{{end}},解析器无法找到闭合标记,直接触发unexpected EOF。同时需处理多返回值的逗号分隔问题,避免语法错误。
修复示例(以第一个return为例):
// 原错误代码 return {{range .ReturnTypes}}{{.Name}}, fmt.Errorf("method {{.Name}} not found") // 修复后代码 return {{range $idx, $rt := .ReturnTypes}}{{if $idx}}, {{end}}{{.Name}}{{end}}, fmt.Errorf("method {{.Name}} not found")
对另外两处return语句执行同样修复。
2. 多返回值的语法缺失
原方法签名中返回值的range遍历未添加逗号,多个返回值会导致语法错误:
// 原错误代码 }) ({{range .ReturnTypes}}{{.Name}} {{.Type}}{{end}}, error) { // 修复后代码 }) ({{range $idx, $rt := .ReturnTypes}}{{if $idx}}, {{end}}{{.Name}} {{.Type}}{{end}}, error) {
3. 结构体初始化的多余逗号
遍历字段生成结构体初始化代码时,最后一个字段会多出逗号,违反Go语法。需添加判断避免:
// 原错误代码 {{range $field := $type.Fields}} {{$field.Name}}: out[{{$i}}].Elem().FieldByName("{{$field.Name}}").Interface().({{$field.Type}}), {{end}} // 修复后代码(需注册sub函数) {{range $idx, $field := $type.Fields}} {{$field.Name}}: out[{{$i}}].Elem().FieldByName("{{$field.Name}}").Interface().({{$field.Type}}){{if ne $idx (sub (len $type.Fields) 1)}},{{end}} {{end}}
注册sub函数(可选)
若使用上述修复,需在解析模板时注册数值减法函数:
funcMap := template.FuncMap{ "sub": func(a, b int) int { return a - b }, } tmpl, err := template.New("server").Funcs(funcMap).Parse(serverTemplate)
完整修复后的服务端模板
package main import ( "context" "fmt" "reflect" "google.golang.org/grpc" ) type {{.InterfaceName}}Server struct { impl interface{} } func New{{.InterfaceName}}Server(impl interface{}) *{{.InterfaceName}}Server { return &{{.InterfaceName}}Server{impl: impl} } {{range .Methods}} func (s *{{$.InterfaceName}}Server) {{.Name}}(ctx context.Context, req struct { {{range .InputParams}}{{.Name}} {{.Type}} {{end}} }) ({{range $idx, $rt := .ReturnTypes}}{{if $idx}}, {{end}}{{.Name}} {{.Type}}{{end}}, error) { method := reflect.ValueOf(s.impl).MethodByName("{{.Name}}") if !method.IsValid() { return {{range $idx, $rt := .ReturnTypes}}{{if $idx}}, {{end}}{{.Name}}{{end}}, fmt.Errorf("method {{.Name}} not found") } in := []reflect.Value{reflect.ValueOf(ctx)} {{range .InputParams}}in = append(in, reflect.ValueOf(req.{{.Name}})){{end}} out := method.Call(in) if len(out) != {{len .ReturnTypes}}+1 { return {{range $idx, $rt := .ReturnTypes}}{{if $idx}}, {{end}}{{.Name}}{{end}}, fmt.Errorf("unexpected number of return values") } if err, ok := out[{{len .ReturnTypes}}].Interface().(error); ok && err != nil { return {{range $idx, $rt := .ReturnTypes}}{{if $idx}}, {{end}}{{.Name}}{{end}}, err } resp := struct { {{range .ReturnTypes}} {{.Name}} {{.Type}} {{end}} }{} {{range $i, $type := .ReturnTypes}} {{if $type.Fields}} resp.{{.Name}} = {{.Type}}{ {{range $idx, $field := $type.Fields}} {{$field.Name}}: out[{{$i}}].Elem().FieldByName("{{$field.Name}}").Interface().({{$field.Type}}){{if ne $idx (sub (len $type.Fields) 1)}},{{end}} {{end}} } {{else}} resp.{{.Name}} = out[{{$i}}].Interface().({{$type.Type}}) {{end}} {{end}} return resp, nil } {{end}} func (s *{{.InterfaceName}}Server) Register(grpcServer *grpc.Server) { {{.InterfaceName}}_RegisterService(grpcServer, s.impl) }
内容的提问来源于stack exchange,提问作者Umang Mundhra
相关产品推荐
相关产品推荐

