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…