JavaScript >> Javascript 文檔 >  >> Node.js

JSON 不再酷了:在 Node.js 中實現協議緩衝區

作為網絡通信協議的無處不在的 JSON 有一個更好的替代方案。它是協議緩衝區(protobuf)。簡而言之,protobuf 提供了更密集的格式(更快的處理)並提供了數據模式(結構的強制執行和與舊代碼更好的兼容性)。

協議緩衝區是由 Google 引入的。您可以在官方的 Protocol Buffers 開發人員指南中閱讀有關它們的更多信息。更短的內容,請閱讀 5 Reasons to Use Protocol Buffers 而不是 JSON For Your Next Service,它將讓您快速了解 protobuf 相對於 JSON 的優勢。

這篇文章的目的不是強調為什麼 protobufs 更好或者向你推銷這個概念。網上有很多文章可以幫到你。本文的目的是向您展示如何在 Node.js 環境中開始使用這種格式。

本文將引導您使用 Node.js、Express.js、Axios 和 Protobuf.js 實現協議緩衝區的 RESTful API 實現。這篇文章中的代碼在 Node v6.2.0 上運行,因為它是用 JavaScript 語言的尖端 ES6/ES2015 版本編寫的。我們將收到一條包含兩個字段 text 的消息 和 lang 從服務器作為 protobuf 發送,解碼並顯示在瀏覽器上。我們還將有一個按鈕,它將向服務器發送另一個 protobuf 消息。源代碼在 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 文件夾是我們所有瀏覽器資產所在的位置。我們有 Axios 從瀏覽器向服務器發出 HTTP 請求。它類似於 Superagent 或 Request。您還可以使用 jQuery 發出 HTTP 請求。如果您要使用 Axios 以外的庫,只需確保將數據作為 ArrayBuffer 提交並作為 application/octet-stream 發送 .

Protobuf.js 是在 JavaScript 和 Node.js 中使用 Google 協議緩衝區的庫,所以我們需要 protobuf.js 瀏覽器上的文件。它需要支持長數字(如您所知,JavaScript 中的數字大小限制為 53 位),並且有一個簡潔的庫允許我們處理 64 位整數,稱為 long.js。

message.proto 是我們將從服務器發送到瀏覽器並返回的消息對象的原型(模式)。它看起來像這樣:

message Message {
    required string text = 1;
    required string lang = 2;
}

Protobuf.js 還需要一個依賴項——bytebuffer.js 對於 ArrayBuffer 數據類型。

格式比較容易理解。我們有兩個字段 textlang .它們都是必填字段。字段名稱旁邊的數字是協議緩​​衝區解碼/編碼所需的。

index.html 具有包含庫的最小 HTML,包括 <pre> 我們將在其中插入來自服務器的響應的容器,觸發 sendMessage() 的按鈕 (我們稍後會寫),以及 <script> 帶有請求和 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 請求。它們都必須使用協議緩衝區。

首先,我們創建Message 從我們的原型文件 message.proto .在loadProtoFile的回調中 我們可以調用 loadMessage() 向服務器發出 GET 請求。

[旁注]

閱讀博客文章很好,但觀看視頻課程更好,因為它們更具吸引力。

許多開發人員抱怨 Node.js 上缺乏負擔得起的高質量視頻材料。觀看 YouTube 視頻會讓人分心,花 500 美元購買 Node 視頻課程很瘋狂!

去看看 Node University,它有關於 Node 的免費視頻課程:node.university。

[旁注結束]

"use strict";
let ProtoBuf = dcodeIO.ProtoBuf
let Message = ProtoBuf
  .loadProtoFile('./message.proto', (err, builder)=>{
    Message = builder.build('Message')
    loadMessage()
  })

Axios 庫將請求的 URL 作為第一個參數,將請求選項作為第二個參數。我們必須提供的選項之一是 arraybuffer .這將告訴 HTTP 代理返回給我們適當的數據類型。 Axios 使用 Promise,所以在 then 回調,我們可以得到response , 記錄並使用 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/octet-stream

至於將協議緩衝區發送到服務器,請確保使用 new Message(data) 創建對象 然後調用 msg.toArrayBuffer() .設置 Content-Type 是個好主意 application/octet-stream 的標頭 所以服務器知道傳入數據的格式:

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 結果 和payload如下圖所示:

我們已經完成了前端,但它不適用於我們的服務器代碼,所以接下來讓我們實現 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 i 安裝依賴項 .它將安裝 express 用於構建 HTTP 服務器和 protobufjs 以在服務器上使用 Protocol Buffers。

讓我們先實現服務器代碼。在 index.js ,我們導入依賴,創建express 對象並為 public 應用靜態中間件 文件夾:

let path = require('path')
let express = require('express')
let app = express()
let publicFolderName = 'public'
app.use(express.static(publicFolderName))

接下來,我們將使用內存存儲來簡化這個項目。換句話說,GET 請求的數據將來自一個數組:

let messages = [
  {text: 'hey', lang: 'english'},
  {text: 'isänme', lang: 'tatar'},
  {text: 'hej', lang: 'swedish'}
]

通常,您會使用 body-parser 用於解析 JSON 請求。為了正確處理傳入的 protobuf,我們需要將其解析為緩衝區數組。讓我們實現我們自己的自定義中間件來解析 protobuf 並將它們存儲在 body.raw (裝飾器模式)。我們需要創建 body.raw 僅當標頭 Content-Typeapplication/octet-stream 當有數據時(data.length>0 ):

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()
  })
})

現在我們可以從原型文件中創建構建器對象和“構建”消息。我們使用相同的原型文件public/message.proto 作為我們的前端代碼:

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

現在我們可以實現 GET,在其中創建一條新消息,對其進行編碼並轉換為 Buffer 類型,然後再發送回前端客戶端。 Express 的 response.send() 正在註意添加正確的“內容類型”。您可以使用 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 解碼 (由我們之前定義的中間件填充),然後登錄終端:

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 複製,您應該會在網頁上看到來自服務器的隨機消息。然後,如果您單擊該按鈕,您應該會在運行 Node.js 服務器的終端/命令提示符中看到“yo”。

而已。我們使用 Protobuf.js 實現了 GET 和 POST 以在 Node.js/Express.js 和瀏覽器 JavaScript 之間的協議緩衝區中進行通信。我們使用 Axios 從瀏覽器發出 HTTP 請求。它允許我們使用 Promise 並抽像出一些用於處理二進制數據的低級 XMLHttpRequest 接口。

Google 將 Protocol Buffers 用於他們的 API。 Protobufs 在許多方面都優於 JSON 或 XML,並且使用 Protobuf.js 和本快速教程,您應該可以很好地開始為您的 RESTful API 使用 Protocol Buffers!


Tutorial JavaScript 教程
  1. 2022 年最受歡迎的 React UI 組件庫

  2. 使用 VueJs 和 Golang 構建視頻聊天應用程序

  3. 只是另一個快速增長的初創公司......

  4. Javascript:如何檢測瀏覽器窗口是否滾動到底部?

  5. JavaScript 類中的私有類字段和方法

  6. 回到 Firefox 歷史後,JavaScript 將無法運行

  7. 將 React 應用程序轉變為具有離線檢測、服務工作者和主題的可安裝 PWA。

  1. 在瀏覽器中將 SVG 轉換為圖像(JPEG、PNG 等)

  2. 用技術抓住 Meta

  3. Angular 9:延遲加載組件

  4. 10 個 jQuery 浮動菜單和消息插件

  5. Node.js WebSocket 教程 - 使用多個客戶端的實時聊天室

  6. 一個簡單的 jquery 初學者計算器

  7. 防止 window.open 聚焦

  1. 讓有眼睛👁 殘疾的人可以訪問您的網站

  2. TypeScript - 應用類型

  3. 在 Discord.JS 中編寫成熟的瑣事遊戲

  4. Meme:Vue Shorthands - 變量和事件綁定