preface
Before has been engaged in Android development, recently the company to small procedures, front and back end data interaction is using protobuf, JS what have been returned to the teacher, anyway, is learning while doing it! Here chose UNIAPP as the development of micro channel small program, look at the introduction of uniAPP official website, feel quite hanging.
Step 1: Protobuf codec
Prepare the proto file first
syntax = "proto3";
package common;
message ErrorInfo {
fixed32 errorCode = 1; / / error code
bytes errorMessage = 2; // Error description
}
message AwesomeMessage{
fixed32 version = 1;
fixed32 app = 2;
fixed32 server = 3;
fixed32 servant = 4;
bytes data = 9;
}
Copy the code
Protobufjs is used for protobuf codec
Go into your project directory and install Protobufjs, which incidentally also installed Axios (later found that axios is not used in wechat mini program)
npm install axios protobufjs --save
Copy the code
Generate static-module js file for proto. In this case, we use static-module to generate JS file for proto. Currently found using jSON-module, using lookup and lookuptype in wechat small program error, H5 can be normal codec.
npx pbjs -t static-module -w commonjs -o proto/bundle.js proto/*.proto
Copy the code
The next step is to use this bundle.js to encode and decode
import {common} from '.. /.. /proto/bundle.js';
let errinfo = common.ErrorInfo.create({
errorCode: 0.errorMessage:"success"
})
Uint8Array (browser) or Buffer (node)
let errBuffer = common.ErrorInfo.encode(errinfo).finish()
// Decode the Uint8Array (browser) or Buffer (node) into ErrorInfo objects
let message = common.ErrorInfo.decode(errBuffer)
// Convert to an object
let obj = common.ErrorInfo.toObject(message, {
enums: String.// enums as string names
longs: String.// longs as strings (requires long.js)
bytes: String,})console.log(obj);
Copy the code
Output result
{errorCode: 0.errorMessage: "success="}
Copy the code
Print out the result to find an extra =, then look at the official document:
var object = AwesomeMessage.toObject(message, {
enums: String.// enums as string names
longs: String.// longs as strings (requires long.js)
bytes: String.// bytes as base64 encoded strings
defaults: true.// includes default values
arrays: true.// populates empty arrays (repeated fields) even if defaults=false
objects: true.// populates empty objects (map fields) even if defaults=false
oneofs: true // includes virtual oneof fields set to the present field's name
});
Copy the code
//bytes as base64 encoded strings
Bytes are base64 encoded strings, so get rid of bytes: String and the result is printed as byte arrays.
{errorCode: 0.errorMessage: Uint8Array(5)}
Copy the code
Error: invalid encoding Error: Invalid encoding
The last modified code looks like this
import {common} from '.. /.. /proto/bundle.js';
let errinfo = common.ErrorInfo.create({
errorCode: 0.errorMessage:Buffer.from('success')})Uint8Array (browser) or Buffer (node)
let errBuffer = common.ErrorInfo.encode(errinfo).finish()
// Decode the Uint8Array (browser) or Buffer (node) into ErrorInfo objects
let message = common.ErrorInfo.decode(errBuffer)
// Convert to an object
let obj = common.ErrorInfo.toObject(message, {
enums: String.// enums as string names
longs: String.// longs as strings (requires long.js)
defaults: true.// Use the default value, otherwise undefined
//bytes: String,
})
console.log(obj);
let buf = Buffer.from(obj.errorMessage)
console.log(`errorMessage = ${buf.toString()}`);
Copy the code
It is also possible to print out the correct result and then run it on the applet emulator
ErrorMessage = successCopy the code
Step 2: Send the protobuf data over HTTP to the server, respond and decode the protobuf to get the result
Next is the login proto file
syntax = "proto3";
package user;
import "Common.proto";
message login_req
{
bytes phone = 1;
uint32 type = 2;
bytes verify_info = 3;
}
message login_rsp {
ErrorInfo errInfo = 1; // Error code information
uint32 id = 2;
bytes token = 3;
}
Copy the code
Next is the proto code, modify or add proTO file after the GENERATION of JS file through PBJS
import {common, user} from '.. /.. /proto/bundle.js';
const axios = require('axios');
let loginMessage = user.login_req.create({
phone: Buffer.from('1234567890'),
type: 1.verifyInfo: Buffer.from('123456a'),// Verify_info is incorrect if the proto file is used
})
// Proto object to buffer
let buffer = user.login_req.encode(loginMessage).finish()
console.log(buffer);
let requestMessage = common.AwesomeMessage.create({
version: 1.app: 1.server: 2.servant: 1005.data: buffer,
})
let requestBuffer = common.AwesomeMessage.encode(requestMessage).finish()
console.log(requestBuffer);
Copy the code
Verify_info = verify_info = verify_info = verify_info = verify_info = verify_info = verify_info = verify_info Then I went to bundle.js and found this as shown below
let loginMessage = user.login_req.create({
phone: Buffer.from('1234567890'),
type: 1.verify_info: Buffer.from('123456a'),// Error should be verifyInfo
})
Copy the code
Proto is better off using the hump naming principle
Next up is the HTTP request using AXIOS at the front end
function transformRequest(data) {
return common.MsgWebsocket.encode(requestMessage).finish()
}
axios.create({
timeout: 15000.method: 'post'.headers: {
"X-Requested-With": "XMLHttpRequest"."Content-Type": "application/octet-stream",},responseType: 'arraybuffer',
}).post('http://********', requestMessage, {
transformRequest: transformRequest
})
.then((response) = > {
console.log(response);
if (response.status === 200) {
try {
let enc = new TextDecoder('utf-8')
let res = JSON.parse(enc.decode(new Uint8Array(response.data))) // Convert to json object
console.log(res);
} catch (e) {
//let resBuf = protobuf.util.newBuffer(response.data)
let resBuf = Buffer.from(response.data)
let resMessage = common.AwesomeMessage.decode(resBuf)
let loginRspBuf = resMessage.data
let loginRspMessage = user.login_rsp.decode(loginRspBuf)
let obj = user.login_rsp.toObject(loginRspMessage, {
longs: String.enums: String,})console.log(obj);
console.log(`errorMessage = ${Buffer.from(loginRspMessage.errInfo.errorMessage).toString()}`); }}},(err) = > {
console.log(err);
});
Copy the code
You can see why it’s a JSON object, isn’t it protobuf? There seems to be a bug in the back end
When the request succeeds, the back end returns an array of protobuf bytes
When the request fails, the back end returns a JSON object
In this case, the HTTP status code returned by both successful and failed requests is 200
Ok, the protobuf result returned by the server is printed
Well, the next is running in the micro channel small program, the result reported an error
So the error is on this line, and it turns out it’s Axios and it’s reporting an error here
You stars, wechat mini program does not support axios!!!!!
Step 3: Send protobuf data to server through HTTP in wechat applet, and then parse protobuf to get results after response
Here uniApp has encapsulated the HTTP request API in basically the same way as the applets HTTP request API
Let me rewrite this as wxhttpTest
uni.request({
url: 'http://xxxxxxxxxxxxx'.header: {
"X-Requested-With": "XMLHttpRequest"."Content-Type": "application/octet-stream",},method: 'POST'.timeout: 15000.dataType: 'protobuf'.// Write whatever you want, as long as it's not JSON
responseType: 'arraybuffer'.data: requestBuffer
}).then((res) = > {
console.log(res);
// Return an array of response, the first is null, and the second is the response returned by the server
// I don't know if it's uniapp or wechat mini program
for (let response of res) {
if(response ! = =null&& response ! = =undefined && response.statusCode === 200) {
try {
let jsonStr = Buffer.from(response.data).toString()
let resJson = JSON.parse(jsonStr) // Convert to json object
console.log(resJson);
} catch (e) {
//let resBuf = protobuf.util.newBuffer(response.data)
let resBuf = Buffer.from(response.data)
let resMessage = common.AwesomeMessage.decode(resBuf)
let loginRspBuf = resMessage.data
let loginRspMessage = user.login_rsp.decode(loginRspBuf)
let obj = user.login_rsp.toObject(loginRspMessage, {
longs: String.enums: String.defaults: true,})console.log(obj);
console.log(`errorMessage = ${Buffer.from(loginRspMessage.errInfo.errorMessage).toString()}`); }}}},(err) = > {
console.log(err);
});
Copy the code
The result is JSON data. The data was entered incorrectly
The protobuf data received by the backend failed to parse, and then returned json error to the small program, which is directly confused, how to do
Keep searching for dafa
This article was found in the development community of wechat
And then I added this
requestBuffer = new Uint8Array([...requestBuffer]).buffer
Copy the code
Actually good, get the protobuf data returned from the back end, and parse it successfully
Finally, Android and ios real machine debugging also passed. New Uint8Array([…requestBuffer]).buffer = Uint8Array([…requestBuffer])
Finally thanks to these posts:
How to use Protobuf in the front end
Axios uses Protobuf to communicate
Axios requests that the responseType be set to ‘blob’ or ‘ArrayBuffer’ when downloading a file, properly handle the case where the return value is a file stream or JSON object
Developers.weixin.qq.com/community/d…