vue中使用protobuf的过程记录(2)

一般来说,前后端需要统一约束一个请求model和响应model,比如请求中哪些字段是必须的,返回体中又有哪些字段,这里用 MessageType.proto 的 PBMessageRequest 来定义请求体所需字段, PBMessageResponse 定义为返回体的字段。

PBMessageType 是接口的枚举,后端所有的接口都写在这里,用注释表示具体请求参数和返回参数类型。比如这里只定义了一个接口 getStudentList 。

拿到后端提供的这份 *.proto 文件后,是不是已经可以基本了解到:有一个 getStudentList 的接口,请求参数是 PBStudentListReq ,返回的参数是 PBStudentListRsp 。

所以说proto文件可以直接作为前后端沟通的文档。

步骤

1.新建一个vue项目

同时添加安装 axios 和 protobufjs 。

# vue create vue-protobuf # npm install axios protobufjs --save-dev

2.在 src 目录下新建一个 proto 目录,用来存放 *.proto 文件,并将写好的proto文件拷贝进去。

此时的项目目录和 package.json :

vue中使用protobuf的过程记录

3.将 *.proto 文件生成 src/proto/proto.js (重点)

protobufjs 提供了一个叫 pbjs 的工具,这是一个神器,根据参数不同可以打包成xx.json或xx.js文件。比如我们想打包成json文件,在根目录运行:

npx pbjs -t json src/proto/*.proto > src/proto/proto.json

可以在 src/proto 目录下生成一个proto.json文件,查看请点击这里。 之前说了:实践证明打包成js模块才是最好用的。我这里直接给出最终的命令

npx pbjs -t json-module -w commonjs -o src/proto/proto.js src/proto/*.proto

-w 参数可以指定打包js的包装器,这里用的是commonjs,详情请各位自己去看文档。运行命令后在src/proto目录下生成的 proto.js 。在chrome中 console.log(proto.js) 一下:

vue中使用protobuf的过程记录

可以发现,这个模块在原型链上定义了 load , lookup
等非常有用的api,这正是后面我们将会用到的。 为以后方便使用,我们将命令添加到package.json的script中:

"scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint", "proto": "pbjs -t json-module -w commonjs -o src/proto/proto.js src/proto/*.proto" },

以后更新proto文件后,只需要 npm run proto 即可重新生成最新的proto.js。

4. 封装request.js

在前面生成了proto.js文件后,就可以开始封装与后端交互的基础模块了。首先要知道,我们这里是用axios来发起http请求的。

整个流程:开始调用接口 -> request.js将数据变成二进制 -> 前端真正发起请求 -> 后端返回二进制的数据 -> request.js处理二进制数据 -> 获得数据对象。

可以说request.js相当于一个加密解密的中转站。在 src/lib 目录下添加一个 request.js 文件,开始开发:

既然我们的接口都是二进制的数据,所以需要设置axios的请求头,使用arraybuffer,如下:

import axios from 'axios' const httpService = axios.create({ timeout: 45000, method: 'post', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/octet-stream' }, responseType: 'arraybuffer' })

MessageType.proto 里面定义了与后端约定的接口枚举、请求体、响应体。发起请求前需要将所有的请求转换为二进制,下面是request.js的主函数

import protoRoot from '@/proto/proto' import protobuf from 'protobufjs' // 请求体message const PBMessageRequest = protoRoot.lookup('framework.PBMessageRequest') // 响应体的message const PBMessageResponse = protoRoot.lookup('framework.PBMessageResponse') const apiVersion = '1.0.0' const token = 'my_token' function getMessageTypeValue(msgType) { const PBMessageType = protoRoot.lookup('framework.PBMessageType') const ret = PBMessageType.values[msgType] return ret } /** * * @param {*} msgType 接口名称 * @param {*} requestBody 请求体参数 * @param {*} responseType 返回值 */ function request(msgType, requestBody, responseType) { // 得到api的枚举值 const _msgType = getMessageTypeValue(msgType) // 请求需要的数据 const reqData = { timeStamp: new Date().getTime(), type: _msgType, version: apiVersion, messageData: requestBody, token: token } } // 将对象序列化成请求体实例 const req = PBMessageRequest.create(reqData) // 调用axios发起请求 // 这里用到axios的配置项:transformRequest和transformResponse // transformRequest 发起请求时,调用transformRequest方法,目的是将req转换成二进制 // transformResponse 对返回的数据进行处理,目的是将二进制转换成真正的json数据 return httpService.post('/api', req, { transformRequest, transformResponse: transformResponseFactory(responseType) }).then(({data, status}) => { // 对请求做处理 if (status !== 200) { const err = new Error('服务器异常') throw err } console.log(data) },(err) => { throw err }) } // 将请求数据encode成二进制,encode是proto.js提供的方法 function transformRequest(data) { return PBMessageRequest.encode(data).finish() } function isArrayBuffer (obj) { return Object.prototype.toString.call(obj) === '[object ArrayBuffer]' } function transformResponseFactory(responseType) { return function transformResponse(rawResponse) { // 判断response是否是arrayBuffer if (rawResponse == null || !isArrayBuffer(rawResponse)) { return rawResponse } try { const buf = protobuf.util.newBuffer(rawResponse) // decode响应体 const decodedResponse = PBMessageResponse.decode(buf) if (decodedResponse.messageData && responseType) { const model = protoRoot.lookup(responseType) decodedResponse.messageData = model.decode(decodedResponse.messageData) } return decodedResponse } catch (err) { return err } } } // 在request下添加一个方法,方便用于处理请求参数 request.create = function (protoName, obj) { const pbConstruct = protoRoot.lookup(protoName) return pbConstruct.encode(obj).finish() } // 将模块暴露出去 export default request

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/7430de6f282cb1efb594d34702fe9f1c.html