是什么东西?
Protocol Buffers 是一种序列化数据结构的协议。对于透过管线(pipeline)或存储数据进行通信的程序开发上是很有用的。这个方法包含一个接口描述语言,描述一些数据结构,并提供程序工具根据这些描述产生代码,用于将这些数据结构产生或解析数据流。
浏览器端使用方法
安装工具库
将后端接口开发提供的 .proto
文件转换成对应的 .js
文件,通过工具的命令 pbjs
1
| npx pbjs -t json-module -w es6 -o src/proto/proto.js src/proto/*.proto
|
执行后会将所有 proto 文件合并成一个 es6 格式的 js 文件,方便在具备打包环境的项目中使用。
封装请求函数
一般来说常用请求就是 post
跟 get
Protocol buffers 需要通过二进制数据进行传输,且需要通过 proto 文件中定义的数据格式来约束请求数据跟响应数据
因此我们在柯里化函数思路里可以考虑一个入参的流程
入参数据定义 -> 响应数据定义 -> 正式发送请求
有了流程我们开始设计第一个 入参数据的定义
1 2 3 4 5 6 7 8 9 10
|
(protoRoot, reqMessageName) => { let reqMessage; if (reqMessageName) { reqMessage = protoRoot.lookup(reqMessageName); } };
|
因为考虑到 get
请求可能并不需要入参因此根据 reqMessageName
来决定是否要描述入参数据
接下来开发 响应数据定义
1 2 3 4 5 6 7
|
return function(resMessageName) { const resMessage = protoRoot.lookup(resMessageName); };
|
最后就是正式的数据请求以及对反馈数据的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
return function(method = 'get', url, data = {}) { let message, sendData; if (method.toLowerCase() === 'post') { message = reqMessage.create(data); sendData = reqMessage.encode(message).finish(); } else { sendData = null; const querySearch = useObjectQueryStringify(data); url += querySearch === '' ? '' : `?${querySearch}`; }
return new Promise((rs, rj) => { const xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.responseType = 'arraybuffer'; xhr.setRequestHeader('Content-Type', 'application/x-protobuf'); xhr.onload = function(response) { const result = resMessage.toObject( resMessage.decode(new Uint8Array(response.target.response)), { bytes: String } ); for (let k in result) { if (~toString.call(result[k]).indexOf('String')) { try { result[k] = atob(result[k]); } catch (e) {} } } rs(result); }; xhr.onerror = function(error) { rj(error); }; xhr.send(sendData); }); };
|
三个主要步骤都有了之后合并成一个 curry function
1 2 3 4 5 6 7 8 9 10 11 12
| export default (protoRoot, reqMessageName) => { let reqMessage; if (reqMessageName) { reqMessage = protoRoot.lookup(reqMessageName); } return function(resMessageName) { const resMessage = protoRoot.lookup(resMessageName); return function(method = 'get', url, data = {}) { }; }; };
|
使用范例
先将上面的函数保存为 usePBRequest.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import protoRoot from './proto/proto.js'; import usePBRequest from './usePBRequest.js';
const commonReq = usePBRequest(protoRoot)('model.CommonRs'); commonReq('get', '/api/get1').then(resp => { console.log(resp); }); commonReq('get', '/api/get2').then(resp => { console.log(resp); });
const postReq = usePBRequest(protoRoot, 'model.CommonReq')( 'model.CommonRs' );
postReq('post', '/api/post1', { userId: 1, token: 'aaaa' }) .then(resp => { console.log(resp); });
postReq('post', '/api/post2', { postId: 1 }) .then(resp => { console.log(resp); });
|
更新函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| import useObjectQueryStringify from './useObjectQueryStringify';
export const createSendData = (data, { protoRoot, reqType }) => { const reqMessage = protoRoot.lookup(reqType); return reqMessage.encode(reqMessage.create(data)).finish(); };
export default protoRoot => ({ reqType, resType }) => { let resMessage; resMessage = protoRoot.lookup(resType);
return function(method = 'get', url, data = {}) { let sendData; if (method.toLowerCase() === 'post') { sendData = createSendData(data, { protoRoot, reqType }); } else { sendData = null; const querySearch = useObjectQueryStringify(data); url += querySearch === '' ? '' : `?${querySearch}`; }
return new Promise((rs, rj) => { const xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.responseType = 'arraybuffer'; xhr.setRequestHeader('Content-Type', 'application/x-protobuf'); xhr.onload = function(response) { const result = resMessage.toObject( resMessage.decode(new Uint8Array(response.target.response)), { bytes: String } ); for (let k in result) { if (~toString.call(result[k]).indexOf('String')) { try { result[k] = atob(result[k]); } catch (e) {} } } rs(result); }; xhr.onerror = function(error) { rj(error); }; xhr.send(sendData); }); }; };
|