JSON已经没有那么流弊了,现在都开始在Node.js中用Protocol Buffers啦

2 篇文章 0 订阅
1 篇文章 0 订阅

原链接:https://webapplog.com/json-is-not-cool-anymore/

声明:csdn强制在图片上加水印,文中所使用图片均来自于作者的博客,本文只做翻译。

       目前,在web文件传输中,有了一种比JSON更好的选择——Protocol Buffers(protobuf)。Protobuf提供了一个更紧凑的文件格式,并且提供一种数据模式(能让这个结构进行扩展,能够跟旧的代码兼容)

       Protocol Buffers由Google提出。你可以在Protocol的官网上找到Protocol Buffers开发指南,如果想知道更多信息,点击为什么在接下来的服务中用Protocol Buffers而不是JSON的5大理由

       这篇文章的用意不是为了介绍protobufs多流弊,也不是为了向你兜售这个概念,网上有很多文章已经干过这个事儿了。这篇文章是叫你咋用,咋在Node.js环境下用。

       这篇文章会带你过一遍Node.js,Express.js,Axios和Protobuf.js的一些API,这篇文章在Node v6.2.0下写成,用了最前沿的ES6/ES2015版本的JavaScript语言。

       我们会创建一个message,message里面包含两个字段:text和lang,这两个字段会作为protobuf从服务器端发送,然后在浏览器端进行解码;我们还会创建一个按钮,这个按钮会将另外的protobuf的message发送到服务器端,代码源码放在github上:azat-co/proto-buffer-api

 

整个项目的结构为:

/proto-buffer-api
  /public
    axios.min.js
    bytebuffer.js
    index.html
    long.js
    message.proto
    protobuf.js
  /node_modules
  index.js
  package.json

        public下放置的是浏览器中所必须的js文件。我们使用Axios来从浏览器向服务器发出请求。这类似于Superagent和Request。你也可以使用JQuery来发出请求。如果你发出请求的库用的不是Axios,只要确保你发出的数据是以ArrayBuffer的形式并且是作为application/octet-stream发出来的就好了。

       protobuf.js文件是Google公司的Protocol Buffers的JavaScript版本,所以我们需要在浏览器中加载这个js文件。JavaScript支持long型数据(在JavaScript中的数字长度只有53bits)。有一个库可以让我们使用64-bit的数字,这个库叫做long.js

       message.proto是message类的原型(也是protobuf一般情况下编译的文件)。这个类就是我们一般在浏览器端和服务器端传送的数据。这个数据一般长这样:

message Message {

    required string text = 1;

    required string lang = 2;

}

       protobuf.js需要一个或者更多的依赖库,例如:bytebuffer.js用来存储ArrayBuffer类型的数据。

       上面的Message的存储格式,应该相当容易理解吧。我们有两个字段,一个text,一个lang。这两个字段都是required类型的字段。紧跟着字段后面的值,就是protocol buffers编解码的内容。

        index.html文件,麻雀虽小,五脏俱全,包含了我们前面讲到的所有的lib文件。<pre>容器中插入来自服务器的响应(response from the server);按钮用来触发sendMessage()函数(这个函数在后面会实现);<script>标签用来处理请求(requests)和protobuf代码。

<html>

  <head>

    <script src="long.js"></script>

    <script src="bytebuffer.js"></script>

    <script src="protobuf.js"></script>

    <script src="axios.min.js"></script>

  </head>

  <body>

    <pre id="content"></pre>

    <button onClick="sendMessage()">send message to server</button>

    <script type="text/javascript">

        // Our requests and Protobuf code

    </script>

  </body>

</html>

       接下来让我们深入探究一下浏览器中JavaScript的两种请求:GET请求来从服务器中获取信息,POST请求来向服务器发送信息。这两者都有用到protocol buffers。

       首先,我们在message.proto文件中创建了一个Message的原型类。在loadProtoFile的回调函数中,我们通过调用loadMessage()的方式来从服务器中GET请求。

[注]

看博客帖子当然很不错啦,不过看视频课程有的时候可能更有感觉。

有不少开发者抱怨,Node.js缺少高质量的视频课程,在YouTube上看,还是花个500刀买课程看,都足够让程序员抓狂啦!

访问Node University,这里有很多Node的课程。

[注完](竟然是广告!!!!不过看起来资源不错,而且也是翻译的人家的,就挂在这儿啦~)

"use strict";

let ProtoBuf = dcodeIO.ProtoBuf

let Message = ProtoBuf

  .loadProtoFile('./message.proto', (err, builder)=>{

    Message = builder.build('Message')

    loadMessage()

  })

       在loadMessage函数中,当然是使用我们之前提到的用于请求/响应的库Axios来设置请求,我们还需要提供一个响应的数据类型:arraybuffer,这个参数能够告诉HTTP,给我们返回正确的数据类型。Axios使用promises的方式运行,所以我们可以使用then函数来运行下一个函数,得到响应后,我们先输出,然后使用Message.decode()完成解码工作。

let loadMessage = ()=> {

  axios.get('/api/messages', {responseType: 'arraybuffer'})

    .then(function (response) {

      console.log('Response from the server: ', response)

      let msg = Message.decode(response.data)

      console.log('Decoded message', msg)

      document.getElementById('content').innerText = JSON.stringify(msg, null, 2)

    })

    .catch(function (response) {

      console.log(response)

    })

}

      GET请求的结果可以在开发者工具(DevTools)中看到。你也可以在application/actet-stream中看到这个响应。

 

       如果想要发送一个protocol buffers的数据给服务器,那么需要创建一个对象:new Message(data),然后调用msg.toArrayBuffer()。最好能够在application/octet-stream中设置Content-Type头,这样服务器就能知道接收到的数据是什么格式的数据。

let sendMessage = ()=>{

  let msg = new Message({text: 'yo', lang: 'slang'})

  axios.post('/api/messages', msg.toArrayBuffer(),

      { responseType: 'arraybuffer',

      headers: {'Content-Type': 'application/octet-stream'}}

    ).then(function (response) {

      console.log(response)

    })

    .catch(function (response) {

      console.log(response)

    })

}

       带有正确Content-Type的POST请求结果和加载过程也可以在开发者工具中找到。

至此,我们的前端工作就已经做完了,简单吧!接下来我们需要用Node/Express来发布代码,这样才能运转起来。

首先,得创建一个package.json,可以直接拷贝下面含有依赖库的文件:

{

  "name": "proto-buffer-api",

  "version": "1.0.0",

  "description": "",

  "main": "index.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "author": "Azat Mardan",

  "license": "MIT",

  "dependencies": {

    "express": "^4.13.4",

    "protobufjs": "^5.0.1"

  }

}

       有了这个package.json文件之后,我们可以通过npm install,然后就会安装用来建立HTTP服务的express和在服务器上使用Protocol Buffers的protobufjs。

       我们先在服务器端写一个js文件,命名为index.js,在这个文件里,我们导入一些库,创建express对象,并且将express对象应用到public文件夹中:

let path = require('path')

let express = require('express')

let app = express()

let publicFolderName = 'public'

app.use(express.static(publicFolderName))

       接下来,为了简化整个项目,我们会开辟一段内存来存储数据。换句话说,上面所述的message会存储在一个数组中:

let messages = [

  {text: 'hey', lang: 'english'},

  {text: 'isänme', lang: 'tatar'},

  {text: 'hej', lang: 'swedish'}

]

       一般情况下,最典型的应用是我们会使用body-parser来解析JSON请求。为了正确地处理protobuf数据,我们需要按照buffers数组的方式进行解析。将之前自定义的解析处理函数应用在解析protobufs中,解析出来的结果存储在body.raw中(decorator模式)。当且仅当Content-Type为application/octet-stream并且有解析出来的数据(data.length > 0)时,会创建一个body.raw来存储数据。

app.use (function(req, res, next) {
  if (!req.is('application/octet-stream')) return next()
  var data = [] // List of Buffer objects
  req.on('data', function(chunk) {
      data.push(chunk) // Append Buffer object
  })
  req.on('end', function() {
    if (data.length <= 0 ) return next()
    data = Buffer.concat(data) // Make one large Buffer of it
    console.log('Received buffer', data)
    req.raw = data
    next()
  })
})

       现在,我们就可以创建builder对象,然后从prototype文件中“build”我们的Message了。还是用之前前端里在public文件夹中的public/message.proto文件。

let ProtoBuf = require('protobufjs')
let builder = ProtoBuf.loadProtoFile(
  path.join(__dirname,
  publicFolderName,
  'message.proto')
)
let Message = builder.build('Message')

这样,我们在GET中就可以创建一个message了,在发送到前端之前,对其进行编码,并将其转化为Buffer类型。Express的response.send()负责添加正确的’Content-Type’头,当然,你也可以使用response.end()。

app.get('/api/messages', (req, res, next)=>{

  let msg = new Message(messages[Math.round(Math.random()*2)])

  console.log('Encode and decode: ',

    Message.decode(msg.encode().toBuffer()))

  console.log('Buffer we are sending: ', msg.encode().toBuffer())

  // res.end(msg.encode().toBuffer(), 'binary') // alternative

  res.send(msg.encode().toBuffer())

  // res.end(Buffer.from(msg.toArrayBuffer()), 'binary') // alternative

})

 

       对于POST请求的处理,我们等于是把前面的过程逆过来。也就是说,从body.raw(之前定义的一个中间件(middleware))。

app.post('/api/messages', (req, res, next)=>{

  if (req.raw) {

    try {

        // Decode the Message

      var msg = Message.decode(req.raw)

      console.log('Received "%s" in %s', msg.text, msg.lang)

    } catch (err) {

      console.log('Processing failed:', err)

      next(err)

    }

  } else {

    console.log("Not binary data")

  }

})



app.all('*', (req, res)=>{

  res.status(400).send('Not supported')

})



app.listen(3000)

       如果跟着我敲了一遍代码,或者你直接从Github上粘了我的这个项目azat-co/proto-buffer-api,你就可以在web page上看到来自服务器随机发送的message。当你点击按钮的时候,你可以在正在运行的服务器端(node的命令行,或者其他什么地方)看到”yo”。

       以上,就是protobuf应用的全部。我们使用GET和POST的方式来在服务器端的Node.js/Express.js和浏览器端的Protobuf.js之间传递protocol buffers。我们用Axios来发从浏览器端发送请求,Axios允许我们使用promises进行处理,并且能够抽象一些the low-level XMLHttpRequest interface来处理我们的二进制数据。

       谷歌使用Protocol Buffers来作为他们的API。Protobufs在很多方面都比JSON和XML文件要高级,这篇文章主要是帮助你快速入门Protobuf.js,希望能够帮你在自己的项目中构建自己的Protocol Buffers的API。

------------------

作者信息:

Azat Mardan

Microsoft MVP | Book and Course Author | Software Engineering Leader

https://www.linkedin.com/in/azatm

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值