4.7.2 插件中读取扩展信息
在本章的第二节我们已经简单讲述过Protobuf插件的工作原理,并且展示了如何生成RPC必要的代码。插件是一个generator.Plugin接口:
type Plugin interface {
// Name identifies the plugin.
Name() string
// Init is called once after data structures are built but before
// code generation begins.
Init(g *Generator)
// Generate produces the code generated by the plugin for this file,
// except for the imports, by calling the generator's methods P, In,
// and Out.
Generate(file *FileDescriptor)
// GenerateImports produces the import declarations for this file.
// It is called after Generate.
GenerateImports(file *FileDescriptor)
}我们需要在Generate和GenerateImports函数中分别生成相关的代码。而Protobuf文件的全部信息都在*generator.FileDescriptor类型函数参数中描述,因此我们需要从函数参数中提前扩展定义的元数据。
pbgo框架中的插件对象是pbgoPlugin,在Generate方法中首先需要遍历Protobuf文件中定义的全部服务,然后再遍历每个服务的每个方法。在得到方法结构之后再通过自定义的getServiceMethodOption方法提取rest扩展信息:
func (p *pbgoPlugin) Generate(file *generator.FileDescriptor) {
for _, svc := range file.Service {
for _, m := range svc.Method {
httpRule := p.getServiceMethodOption(m)
...
}
}
}在讲述getServiceMethodOption方法之前我们先回顾下方法扩展的定义:
extend google.protobuf.MethodOptions {
HttpRule rest_api = 20180715;
}pbgo为服务的方法定义了一个rest_api名字的扩展,在最终生成的Go语言代码中会包含一个pbgo.E_RestApi全局变量,通过该全局变量可以获取用户定义的扩展信息。
下面是getServiceMethodOption方法的实现:
func (p *pbgoPlugin) getServiceMethodOption(
m *descriptor.MethodDescriptorProto,
) *pbgo.HttpRule {
if m.Options != nil && proto.HasExtension(m.Options, pbgo.E_RestApi) {
ext, _ := proto.GetExtension(m.Options, pbgo.E_RestApi)
if ext != nil {
if x, _ := ext.(*pbgo.HttpRule); x != nil {
return x
}
}
}
return nil
}首先通过proto.HasExtension函数判断每个方法是否定义了扩展,然后通过proto.GetExtension函数获取用户定义的扩展信息。在获取到扩展信息之后,我们再将扩展转型为pbgo.HttpRule类型。
有了扩展信息之后,我们就可以参考第二节中生成RPC代码的方式生成REST相关的代码。
学员评价