上一篇主要总结了环境搭建的流程及在过程中遇到的坑,这一篇详细研究下小程序和服务器通信间的原理。
先分析下上图中三个实验代码:
1. https:
小程序只支持
https
访问,想访问https
一定是要在自己的服务器上配置证书的,小程序成长之路(四)-- 深入腾讯云(环境搭建) ,中有详细说明,这里就不多赘述。
小程序有自己调用接口的方法 wx.request
:
//this.data.url 为 https://www.winsonye.club/hello
wx.request({
url: this.data.url,
method: 'GET',
success: (res) => {
},
//下面省略
});
服务器端:
// 实现一个中间件,对于未处理的请求,都输出 "Response from express"
app.use((request, response, next) => {
response.write('Response from express');
response.end();
});
2. 会话
以前web端的开发,都是通过
cookie
中存储sessionid
,在每次请求时带上cookie
中存储的信息来保持会话,但是小程序不支持cookie
, 需要自己实现会话,我们来看下如何实现:
上面字面是不是很不好理解 ?说白了就是在客户端文件中引入一个官方提供的文件:wafer-client-sdk/index
然后在代码中引用:
const wafer = require('../../vendors/wafer-client-sdk/index');
把 wx.request
换做 wafer.request
小程序的网络请求就会包含会话。
注意:
在使用 wafer.request
前要设置登陆地址:
wafer.setLoginUrl(`https://` + config.host + '/login');
然后进行登陆操作:
qcloud.login({
success: function (userInfo) {
console.log('登录成功', userInfo);
},
fail: function (err) {
console.log('登录失败', err);
}
});
或者省略此步,直接在 wafer.request
中设置 login: true
:
wafer.request({
//request 方法也支持 login 参数支持在请求之前自动登录
login: true,
url: this.data.url,
method: 'GET',
success: (res) => {},
});
注意:
这里有个小坑说一下:
wx.getUserInfo()
调用这个方法获取用户信息时,之前会在小程序上自动弹出授权窗口,但是现在改了,这个方法依然可以使用,但是不会自动弹出授权窗口了。
就是说, 以前没有授权的情况下,调用方法,自动弹出授权窗口,授权后拿到用户信息,完美!而现在,调用时会先给你提示,点击继续使用直接报错!!
如何解决呢?
在页面上加上授权按钮 <button open-type="getUserInfo">授权</button>
,点击授权弹出授权弹窗,确认授权后在调用wx.getUserInfo()
,返回正常。
回到正题,接着上面客户端发送请求的方法 wafer.request
准备好后,看下服务端需要准备的:
- wafer-node-session: 引用 wafer-session 支持小程序会话(服务端)
- connect-mongo:使用 MongoDB 作为会话的存储
// 引用 wafer-session 支持小程序会话
const waferSession = require('wafer-node-session');
// 使用 MongoDB 作为会话的存储
const MongoStore = require('connect-mongo')(waferSession);
......//省略无关代码
// 独立出会话中间件给 express 和 ws 使用
const sessionMiddleware = waferSession({
appId: config.appId,
appSecret: config.appSecret,
loginPath: '/login',
store: new MongoStore({
url: `mongodb://${config.mongoUser}:${config.mongoPass}@${config.mongoHost}:${config.mongoPort}/${config.mongoDb}`
})
});
app.use(sessionMiddleware);
// 在路由 /me 下,输出会话里包含的用户信息
app.use('/me', (request, response, next) => {
response.json(request.session ? request.session.userInfo : { noBody: true });
if (request.session) {
console.log(`Wafer session success with openId=${request.session.userInfo.openId}`);
}
});
前后端准备完毕,点击发送请求,看下返回结果:
上述返回结果中已经带有用户信息,会话信息产生!!
3. websocket
先跟大家分析一下微信支持 WebSocket 的原因(摘自--微信小程序云端解决方案探索之路)。
传统的 HTTPS 连接请求,每个请求都需要建立一次连接,耗费比较多的资源。同时微信有最大连接数的限制(5个),所以实时通信的需求不好做,长连接的方案也只能串行传输,这种方案耗电高体验差。
当我们把目光转向 WebSocket 之后,会发现 WebSocket 通信全程只需要建立一次连接,就可以实现双向的实时通信,更省电的情况下获得更好的体验。
这就是小程序支持 WebSocket 的一个重要原因,可以提高业务的体验并增加续航。
客户端:
- 微信小程序客户端是有一套
websocket
的方法的: websocket
wx.connectSocket
建立链接wx.sendSocketMessage
发送信息wx.onSocketOpen
监听WebSocket连接打开事件wx.onSocketMessage
监听服务器返回信息
。。。等等
服务端:
- 需要引入
ws
模块。
监听客服端触发的事件:
ws.on('connection', function() {...})
ws.on('message', function() {...})
ws.on('close', function() {...})
发送信息:
ws.send('xxxxxxx')
// 引入 ws 支持 WebSocket 的实现
const ws = require('ws');
// 导出处理方法
exports.listen = listen;
/**
* 在 HTTP Server 上处理 WebSocket 请求
* @param {http.Server} server
* @param {wafer.SessionMiddleware} sessionMiddleware
*/
function listen(server, sessionMiddleware) {
// 使用 HTTP Server 创建 WebSocket 服务,使用 path 参数指定需要升级为 WebSocket 的路径
const wss = new ws.Server({ server, path: '/ws' });
// 监听 WebSocket 连接建立
wss.on('connection', (ws,request) => {// 要升级到 WebSocket 协议的 HTTP 连接
// 被升级到 WebSocket 的请求不会被 express 处理,
// 需要使用会话中间节获取会话
sessionMiddleware(request, null, () => {
const session = request.session;
if (!session) {
// 没有获取到会话,强制断开 WebSocket 连接
ws.send(JSON.stringify(request.sessionError) || "No session avaliable");
ws.close();
return;
}
// 保留这个日志的输出可让实验室能检查到当前步骤是否完成
console.log(`WebSocket client connected with openId=${session.userInfo.openId}`);
serveMessage(ws, session.userInfo);
});
});
// 监听 WebSocket 服务的错误
wss.on('error', (err) => {
console.log(err);
});
}
/**
* 进行简单的 WebSocket 服务,对于客户端发来的所有消息都回复回去
*/
function serveMessage(ws, userInfo) {
// 监听客户端发来的消息
ws.on('message', (message) => {
console.log(`WebSocket received: ${message}`);
ws.send(`Server: Received(${message})`);
});
// 监听关闭事件
ws.on('close', (code, message) => {
console.log(`WebSocket client closed (code: ${code}, message: ${message || 'none'})`);
});
// 连接后马上发送 hello 消息给会话对应的用户
ws.send(`Server: 恭喜,${userInfo.nickName}`);
}
通过客服端和服务端相互监听消息事件,进行信息传输。