小程序与MySQL数据库的交互_微信小程序与服务器的交互原理

1. 在linux服务器中配置nginx

简单说,微信小程序要正常运行起来,就要跟服务器进行数据交互,微信小程序服务器配置需要使用https安全域名,现在https证书可以免费申请,我用的是阿里云服务器,可以在阿里云上申请免费的https证书。

关于阿里云服务器配置nginx,请参见《在linux上通过nginx配置微信小程序服务器》。

pm2的安装使用

在nginx配置微信小程序服务器的文章里,我们安装的pm2,是一个能够启动运行nodejs项目的工具,内建负载均衡(使用 Node cluster 集群模块)。

安装很简单:

npm install -g pm2

安装完成之后输入:

pm2 start app.js

问题来了:

-bash: pm2: command not found

提示没有pm2这个命令,这是怎么回事呢?回头查看安装完成时的信息:

安装命令执行完成时候请注意终端输出内容

npm install -g pm2

/www/node-v8.2.1-linux-x64/bin/pm2 -> /www/node-v8.2.1-linux-x64/lib/node_modules/pm2/bin/pm2

/www/node-v8.2.1-linux-x64/bin/pm2-dev -> /www/node-v8.2.1-linux-x64/lib/node_modules/pm2/bin/pm2-dev

/www/node-v8.2.1-linux-x64/bin/pm2-docker -> /www/node-v8.2.1-linux-x64/lib/node_modules/pm2/bin/pm2-docker

/www/node-v8.2.1-linux-x64/bin/pm2-runtime -> /www/node-v8.2.1-linux-x64/lib/node_modules/pm2/bin/pm2-runtime

第一行箭头后面部分是/www/node-v8.2.1-linux-x64/lib/node_modules/pm2/bin/pm2,是pm2的安装目录,显然pm2默认目录不是全局的,下面设置pm2的安装目录软连接到全局下:

ln -s /www/node-v8.2.1-linux-x64/lib/node_modules/pm2/bin/pm2 /usr/local/bin

pm2设置为全局,启动一个项目:

pm2 start app.js

[PM2] Spawning PM2 daemon with pm2_home=/root/.pm2

[PM2] PM2 Successfully daemonized

[PM2] Starting /data/release/weapp/app.js in fork_mode (1 instance)

[PM2] Done.

┌──────────┬────┬─────────┬──────┬───────┬────────┬─────────┬────────┬─────┬──────────┬──────┬──────────┐

│ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │

├──────────┼────┼─────────┼──────┼───────┼────────┼─────────┼────────┼─────┼──────────┼──────┼──────────┤

│ app │ 0 │ 1.0.0 │ fork │ 25378 │ online │ 0 │ 0s │ 0% │ 6.8 MB │ root │ disabled │

└──────────┴────┴─────────┴──────┴───────┴────────┴─────────┴────────┴─────┴──────────┴──────┴──────────┘

Use `pm2 show ` to get more details about an app

2. 微信小程序与服务器交互的流程及原理

官方demo服务端源码解析(了解即可,可略过)

查看小程序服务端的源码,不难发现,在服务器上配置数据库及预创建需连接的数据库是在

\server\tools\cAuth.sql中实现的:

SET NAMES utf8;

SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------

-- Table structure for `cSessionInfo`

-- ----------------------------

DROP TABLE IF EXISTS `cSessionInfo`;

CREATE TABLE `cSessionInfo` (

`open_id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,

`uuid` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,

`skey` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,

`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

`last_visit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

`session_key` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,

`user_info` varchar(2048) COLLATE utf8mb4_unicode_ci NOT NULL,

PRIMARY KEY (`open_id`),

KEY `openid` (`open_id`) USING BTREE,

KEY `skey` (`skey`) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会话管理用户信息';

SET FOREIGN_KEY_CHECKS = 1;

然后,在\server\tools\initdb.js中通过knex实例化我们的mysql数据库对象,通过对象DB的raw方法执行我们上面的SQL语句,执行成功后,将在cAuth数据库中创建成功cSessionInfo表:

/**

* 腾讯云微信小程序解决方案

* Demo 数据库初始化脚本

* @author Jason

*/

const fs = require('fs')

const path = require('path')

// 导入config.js文件作为config对象,调用其中的配置参数

const { mysql: config } = require('../config')

console.log('\n======================================')

console.log('开始初始化数据库...')

// 初始化 SQL 文件路径

const INIT_DB_FILE = path.join(__dirname, './cAuth.sql')

const DB = require('knex')({ //通过knex框架链接数据库并实例化为DB对象

client: 'mysql',

connection: {

host: config.host,

port: config.port,

user: config.user,

password: config.pass,

database: config.db,

charset: config.char,

multipleStatements: true

}

})

console.log(`准备读取 SQL 文件:${INIT_DB_FILE}`)

// 读取 .sql 文件内容

const content = fs.readFileSync(INIT_DB_FILE, 'utf8')

console.log('开始执行 SQL 文件...')

// 执行 .sql 文件内容

DB.raw(content).then(res => {

console.log('数据库初始化成功!')

process.exit(0)

}, err => {

throw new Error(err)

})

以上便是官方测试接口程序在服务端的数据库创建脚本程序。

编写小程序客户端与服务端交互的代码(重点)

首先,我们还是要理顺一下小程序客户端与服务端交互的流程与原理,如下图:

1bcb8a051cee

图2.1:客户端与服务端交互原理图

我自己是通过sequelize框架对数据库进行增删改查的(sequelize和knex都是nodejs框架,用于数据库操作的模块),至于官方demo时通过脚本的方式对数据库初始化,我们完全可以直接在mysql命令行对数据库及表结构进行创建:

//在mysql中用原生sql语句创建数据库

mysql>create database meetdata;

// 在数据库中以原生sql语句生成数据库的表结构,以及定义其字段和数据类型

mysql>create table joiners(

id varchar(50) COLLATE utf8mb4_unicode_ci not null,

name varchar(50) COLLATE utf8mb4_unicode_ci not null,

gender bool not null,

phone bigint not null,

company varchar(100) COLLATE utf8mb4_unicode_ci not null,

imgPath varchar(100) COLLATE utf8mb4_unicode_ci not null,

createdAt bigint not null,

updatedAt bigint not null,

primary key(id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='备注信息';

上面只能算是对服务器中数据库的创建和表结构的创建,下面是小程序客户端与服务端交互(数据发送和请求):

小程序端上传表单数据和文件的源码(结合图2.1):

(1)在客户端上传form表单数据和文件或图片(通过wx.uploadFile()实现的示例代码):

// 提交数据,包括form表单中的数据,和上传的图片到指定的URL中(POST请求方式)

formSubmit: function (e) {

var that = this;

uploadFile(

"https://www.joyitsai.cn/weapp/uploadFile", // 上传文件到指定url

tongzhiImgPath, //wx.chooseImage()的缓存图片地址

"uploadImg", //上传文件时的备注名

{ // 上传文件时可以将附加的表单数据提交到url,formData为{}格式

name: e.detail.value.meetname,

zhuban: e.detail.value.meetZhuban,

chengban: e.detail.value.meetChengban,

jieshao: e.detail.value.meetjieshao,

file1: e.detail.value.file1,

file2: e.detail.value.file2,

latitude: Latitude,

longitude: Longitude,

Location: location

});

// 暂时没有更好的上传多张图片的方式,暂且用多个uploadFile上传多张图片

uploadFile(

"https://www.joyitsai.cn/weapp/uploadImage",

logoImgPath,

"logo",

{

});

}

(2)在服务器配置路由分发器:

接着在服务端的路由配置文件中,配置与客户端对应的URL路由:

/*

ajax 服务路由集合

*/

// 配置在客户端的请求URL

const router = require('koa-router')({

prefix: '/weapp'

})

const controllers = require('../controllers')

// 从 sdk 中取出中间件

// 这里展示如何使用 Koa 中间件完成登录态的颁发与验证

const { auth: { authorizationMiddleware, validationMiddleware } } = require('../qcloud')

// 图片上传接口,对应小程序中url的"https://www.joyitsai.cn/weapp/uploadImage"

router.post('/uploadImage', controllers.uploadImage)

// 文件上传接口,对应小程序中url的"https://www.joyitsai.cn/weapp/uploadFile"

router.post('/uploadFile', controllers.uploadFile)

(3)编写接受数据存储数据的服务器js脚本:

const Sequelize = require('sequelize');

const multer = require('koa-multer');

const config = require('../config');

let path = require('path')

var meetImgPath = '';

var meet_name = '';

var zhuban_company = '';

var chengban_company = '';

var now = null;

var filetitle = '';

// 创建一个Sequelize对象实例,并通过config.js文件中的连接数据库参数,将sequelize连接到指定数据库

// 同时说明以mysql的驱动进行操作(因为我们用的是mysql数据库)

var sequelize = new Sequelize(config.mysql.userdb, config.mysql.user, config.mysql.pass, {

host: config.mysql.host,

dialect: 'mysql',

pool: {

max: 5,

min: 0,

idle: 30000

}

});

const storage = multer.diskStorage({

destination: (req, file, cb) => { //声明文件存储位置

meetImgPath = path.resolve(__dirname, '../uploadFiles/');

console.log(req.body);

// 从request请求中获取上传的文件名

name = req.body.name

// 上传的文件存储的路径

cb(null, path.resolve(__dirname, '../uploadFiles/'));

},

// 定义文件名,file即在小程序端上传的原文件

filename: (req, file, cb) => {

//.pop()弹出文件扩展名,合并成存储在服务器上时的文件名

cb(null, `${name}${filetitle}.${file.originalname.split('.').pop()}`);

}

});

// 为multer(opts)定义的opts参数

const uploadCfg = {

storage: storage,

limits: {

//上传文件的大小限制,单位bytes

fileSize: 1024 * 1024 * 20

}

};

module.exports = async (req, res) => {

console.log('you are uploading the user informations into the mysql...');

let upload = multer(uploadCfg).any();

upload(req, res, async (err) => {

if(err){

console.log(err);

return;

}else{

console.log('the file has uploaded into' + path.resolve(__dirname, '../uploadFiles/${uploadFile.filename}'));

}

})

}

小程序端向服务器请求数据(结合图2.1):

(1)通过wx.request()向指定url请求数据

onLoad: function () {

var that = this;

// wx.request时GET请求

wx.request({

url: 'https://www.joyitsai.cn/weapp/downloads',

data: {

},//给服务器传递数据,本次请求不需要数据,可以不填

header: {

// 默认值,返回的数据设置为json数组格式

'content-type': 'application/json'

},

success: function (res) {

var data = res.data;

if (data.data.downloads) {

var downloads = data.data.downloads;

for(let i=0; i

// 下面就是通过请求获取的json合适的数据

// 依据具体的需求,获取需要的数据,传递到前端

var download = downloads[i];

}

that.setData({

proList: filelist,

})

}

}, // success: function(){}

fail: function (res) {

console.log('下载失败');

}

});

(2)在服务器为小程序端的请求url分配路由:

/*

ajax 服务路由集合

*/

// 配置在客户端的请求URL

const router = require('koa-router')({

prefix: '/weapp'

})

const controllers = require('../controllers')

// 从 sdk 中取出中间件

// 这里展示如何使用 Koa 中间件完成登录态的颁发与验证

const { auth: { authorizationMiddleware, validationMiddleware } } = require('../qcloud')

// 图片上传接口,对应小程序中url的"https://www.joyitsai.cn/weapp/uploadImage"

router.post('/uploadImage', controllers.uploadImage)

// 文件上传接口,对应小程序中url的"https://www.joyitsai.cn/weapp/uploadFile"

router.post('/uploadFile', controllers.uploadFile)

// 小程序端数据请求接口,对应wx.request中url的'https://www.joyitsai.cn/weapp/downloads'

router.get('/downloads', controllers.downloads)

(3)编写服务器请求数据库数据并返回给小程序端的js脚本:

const Sequelize = require('sequelize');

const multer = require('koa-multer');

const config = require('../config');

let path = require('path')

// 创建一个Sequelize对象实例,并通过config.js文件中的连接数据库参数,将sequelize连接到指定数据库

// 同时说明以mysql的驱动进行操作(因为我们用的是mysql数据库)

var sequelize = new Sequelize(config.mysql.userdb, config.mysql.user, config.mysql.pass, {

host: config.mysql.host,

dialect: 'mysql',

pool: {

max: 5,

min: 0,

idle: 30000

}

});

// 定义一个数据模型Download,告诉sequelize如何按照字段及其数据类型去映射到downloads数据库

var Download = sequelize.define('meetlist', {

id: {

type: Sequelize.STRING(50),

primaryKey: true

},

name: Sequelize.STRING(100),

file1: Sequelize.STRING(100),

file2: Sequelize.STRING(100),

createdAt: Sequelize.BIGINT,

updatedAt: Sequelize.BIGINT,

latitude: Sequelize.DOUBLE,

longitude:Sequelize.DOUBLE,

Location: Sequelize.STRING(100),

logo: Sequelize.STRING(100)

},{ timestamps: false }

);

module.exports = async(ctx) => {

var download_list = new Array();

// 查找数据库中所有joiner数据

var downloads = await Download.findAll({

});

for (let download of downloads) {

// 对downloads中每个元素按一定需求进行处理后

// 返回所需的格式

download_list.push(download);

}

console.log(`find ${downloads.length} downloads:`);

let url=ctx.url; //获取url

// 从上下文中直接获取数据

let ctx_query = ctx.query; //query返回格式化的对象

let ctx_querystring = ctx.querystring; //querystring返回原字符

// 从上下文的request对象中获取

let request=ctx.request;

let req_query=request.query; //query返回格式化好的对象

let req_querystring=request.querystring; //querystring返回原字符串。

ctx.body={

data: {downloads: download_list},

url,

ctx_query,

ctx_querystring,

req_query,

req_querystring

}

}

(4)关于ctx,简单的console.log一下,就知道它是什么了:

{ request:

{ method: 'GET',

url: '/weapp/downloads',

header:

{ connection: 'upgrade',

host: 'www.joyitsai.cn',

pragma: 'no-cache',

'cache-control': 'no-cache',

'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1 wechatdevtools/1.02.1811290 MicroMessenger/6.7.3 Language/zh_CN webview/ token/c6a74c101254905a6526830ac4c466aa',

'content-type': 'application/json',

accept: '*/*',

referer: 'https://servicewechat.com/wx80d2948ba1252c7d/devtools/page-frame.html',

'accept-encoding': 'gzip, deflate, br' } },

response: { status: 404, message: 'Not Found', header: {} },

app: { subdomainOffset: 2, proxy: false, env: 'development' },

originalUrl: '/weapp/downloads',

req: '',

res: '',

socket: '' }


版权声明:本文为weixin_30821881原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。