You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 23:35:03