人工智能 大数据 计算机科学

微信小程序与服务器的交互原理

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 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服务端源码解析(了解即可,可略过)

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;
/**
* 腾讯云微信小程序解决方案
* 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:客户端与服务端交互原理图
  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='备注信息';
  1. 上面只能算是对服务器中数据库的创建和表结构的创建,下面是小程序客户端与服务端交互(数据发送和请求):
// 提交数据,包括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}'));
        }
    })
}
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>' }
上一篇下一篇

猜你喜欢

热点阅读