Protocol Buffer在Node.js中的应用

1. Protocol Buffer 简介

先引用官网的一段话:

Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.

协议缓冲区(Protocol Buffe)是Google的与语言无关,与平台无关的可扩展机制,用于对结构化数据进行序列化(例如XML),但更小,更快,更简单。您定义要一次构造数据的方式,然后可以使用生成的特殊源代码轻松地使用各种语言在各种数据流中写入和读取结构化数据。

其实我并不真正理解官网对其的定义,但是在我们的 Node.js 项目中,我一般将其当作 过滤器 来使用,更多的是让它发挥一种规范数据结构的作用,以防止不被期望的数据破坏我们的接口逻辑。所以,想深入学习的小伙伴可以参考官网, 官方文档在此

2. 使用场景

我们在开发某一接口时,通常会先定义好请求和响应的数据结构,并且我们希望我们的接口是按照预期工作的,所以我们会写好接口文档给调用接口的开发者看,但是文档和口头约定并不是一种 强约束 ,我们不能凭此就认为接口的输入是我们预期的那样,所以我们在处理接口的请求参数时需要做一些检查来排除不被预期的参数,比如你有一个接口 list 用来查询某一数据列表,如果这个接口查询条件比较简单,我们可能直接用 for 循环遍历 req.query 来构造我们的 sql ,但是如果里面多了一些其他的参数,可能 list 接口就不能返回预期的数据,导致接口的鲁棒性非常差。所以在接口开始工作前,对请求参数的处理时非常必要的,这时,如果我们事先定义好了属于该接口的 .proto 文件,我们就可以通过它去过滤掉我们不需要的,或者未定义的参数。

3. Protocol 3 语法

我们可以通过一个简单的示例来让大家有一个初步的认识:

// 数据结构
{
  name: 'Bing Xu',
  age: 24,
  hobbies: ['game', 'coding', 'reading'],
  major: {
           name: 'IS',
           period: '4years'
          },
  done: 1 // 是否毕业,1 是 2 否 代表这个字段的值是可枚举的
}

// proto
syntax = "proto3"; // 申明版本,否则会默认proto2

message Information {
  required string name = 1;  // required 表示该字段必填
  optional int32 age = 2;    // optional 表示可选字段
  repeat string hobbies = 3;   // repeat 表示后面的数据结构是可以重复的,也就是数组
  
  message Major {  // 这里面又定义了一种结构
    required string name = 1;
    required string period = 2;
  }
  optional Major major = 4;  // 在这里使用上面定义的结构

  enum Done { // 用enum关键字定义可枚举结构
    YES = 1 // 已完成
    NO = 2 // 未完成
  }
  optional Done done = 5;
}

这里有一个大坑就是在 Node.js 中使用 proto 需要注意的是,字段前面的类似于 optionalrequired 是必须要写的,不写会报错,但是在其他语言中,比如在 Golang 中使用就没有这种限制。更多语法请参考 官方文档

4. 调用方法

我们一般会在项目中封装好调用的方法,我在这里写一个简单的示例:

const ProtoBuf = require("protobufjs");

function asyncProto(message, body) {
 // 这里其实不可能传入message对象的,一般会传一个名称,然后动态生成path,通过load方法去拿到message
  return new Promise((resolve, reject) => {
    const err = message.verify(body)
    if (err) return reject(err)  // 如果验证出错了,说明数据不符合该数据结构
    data = messate.create(body) // 如果验证通过,会使用该数据结构重新生成一份数据,过滤掉不要的参数
    resolve(data)
  });
}

// 然后我们可以在接口里使用
async function list (req, res) {  
  const query = await asyncProto(message, req.query) // 过滤得到符合要求的查询条件
  // do something
}

5. 结束语

本篇文章写得比较粗糙,因为笔者也没有深入的学习,只是大概看了下文档,在语法方面,应该能满足大部分需求了~希望能够对读者有一定的帮助。

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章