微信小程序与服务器的交互原理
2018-12-13 本文已影响0人
joyitsai
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 <id|name>` 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)
})
以上便是官方测试接口程序在服务端的数据库创建脚本程序。
编写小程序客户端与服务端交互的代码(重点)
首先,我们还是要理顺一下小程序客户端与服务端交互的流程与原理,如下图:
图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<downloads.length; 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: '<original node req>',
res: '<original node res>',
socket: '<original node socket>' }