微信小程序源碼解析


最近在寫小程序,轉載一些別人的文章,學習下經驗。

學以致用123 
2018.05.09 13:46 字數 1536 閱讀 475評論 0

關鍵字:微信小程序登錄、login、request、getuserinfo

本文是對使用 wafer2 實現登錄的分析,對使用 wafer2 框架的小程序適用,不涉及 騰訊雲音視頻登錄功能外的其它功能。

參考:https://github.com/tencentyun/wafer2-client-sdk

簡介

騰訊雲音視頻小程序的 demo 代碼使用的是騰訊雲 wafer2 框架。

wafer2 簡化了用戶登錄方法,在 app.js 中設置 qcloud 的 LoginUrl 然后調用 qcloud.login 或者使用 login 參數設置為 true 的 qcloud.request 請求即可。

這里先貼出經過下面分析得出的使用 qcloud 的 login 和 request 的方法。

首先,在 app.js 中設置 qcloud 的 LoginUrl :

qcloud.setLoginUrl(config.url + 'login'); 

如果只需要后台實現登錄請求,那么使用 qcloud.login:

qcloud.login({
    success: function (userInfo) { console.log('登錄成功', userInfo); }, fail: function (err) { console.log('登錄失敗', err); } }); 

setLoginUrl 中設定的 url 的后台視圖需要解析 login 的 GET請求的請求頭獲得code、encryptedData 和 iv ,返回的響應至少需要包含 code,如果成功執行,code 需要設為 0 而且還需要返回 data ,data 中至少需要包含 skey 、userinfo 和 iminfo 三項內容。

如果后台請求之前需要自動登錄,那么使用 qcloud.request :

qcloud.setLoginUrl(myloginUrl);
qcloud.request({
    login: true, url: myUrl, success: function (response) { console.log(response); }, fail: function (err) { console.log(err); } }); 

注意,qcloud.request 通過構造請求頭向后台傳送數據。

下面的內容是具體代碼分析。

現有代碼分析

demo 代碼並沒有使用 wafer2 內置的登錄函數,而是在與 config.js 同一個目錄下的 getloginInfo.js 中通過 wx.login 和 wx.getUserInfo 實現登錄的。這里的實現代碼為:

// 獲取微信登錄信息,用於獲取openid function getLoginInfo(options) { wx.login({ success: function (res) { if (res.code) { console.log('獲取code成功',res.code); options.code = res.code; // 獲取用戶信息 wx.getUserInfo({ withCredentials: false, success: function (ret) { options.userName = ret.userInfo.nickName; proto_getLoginInfo(options); }, fail: function() { proto_getLoginInfo(options); } }); } else { console.log('獲取用戶登錄態失敗!' + res.errMsg); options.fail && options.fail({ errCode: -1, errMsg: '獲取用戶登錄態失敗,請退出重試' }); } }, fail: function (res) { console.log('獲取用戶登錄態失敗!' + res.errMsg); if (ret.errMsg == 'request:fail timeout') { var errCode = -1; var errMsg = '網絡請求超時,請檢查網絡狀態'; } options.fail && options.fail({ errCode: errCode || -1, errMsg: errMsg || '獲取用戶登錄態失敗,請退出重試' }); } }); } 

這里的代碼邏輯非常清晰,用流程圖表示是這樣的:


 
getlogininfo_wxlogin.jpg

上述流程中 wx.login 及 wx.getUserInfo 的詳細信息請查看官網文檔。

代碼執行成功會調用 proto_getLoginInfo 函數,如果失敗,則運行調用輸入參數的失敗處理函數。

proto_getLoginInfo 在 getLoginInfo 的下面,主要實現騰訊雲的 RoomService 登錄功能,但是由於 RoomService 登錄需要傳入后台計算得到的用戶簽名,這里使用了 wx.request 向后台發送 GET 請求,發送請求的具體代碼為:

function proto_getLoginInfo(options) { wx.request({ url: config.serverUrl + '/weapp/utils/get_login_info', data: { userIDPrefix: 'weixin', code: options.code }, method: 'GET', header: { 'content-type': 'application/json' // 默認值 }, success: function (ret) { if (ret.data.code) { console.log('獲取登錄信息失敗,調試期間請點擊右上角三個點按鈕,選擇打開調試'); options.fail && options.fail({ errCode: ret.data.code, errMsg: ret.data.message + '[' + ret.data.code + ']' }); return; } console.log('獲取IM登錄信息成功: ', ret.data); ... }, fail: function (ret) { console.log('獲取IM登錄信息失敗: ', ret); if (ret.errMsg == 'request:fail timeout') { var errCode = -1; var errMsg = '網絡請求超時,請檢查網絡狀態'; } options.fail && options.fail({ errCode: errCode || -1, errMsg: errMsg || '獲取登錄信息失敗,調試期間請點擊右上角三個點按鈕,選擇打開調試' }); } }); } 

這里的 wx.request 向后台發送 code 參數,后台使用 code 換取 openid 然后計算用戶簽名,代碼中的登錄功能結束。

然而, wafer2 的處理用戶登錄流程是這樣的:

 
wxlogin.png

由於這里指向后台傳入了 code ,所以后台只能實現紅色框中的內容。前台也沒有實現紫色框中的保存會話功能。

我們可以將 wx.getUserInfo 的 withCredentials 設置為 true ,然后為 wx.request 傳入 encryptedData 和 iv ,完成上圖中后台的工作,並使用 wx.setStorageSync(KEY,DATA) 保存會話。

然而,我們完全可以使用 wafer2 內置函數實現登錄。

使用 wafer2 內置函數實現登錄

查看 app.js 文件,我們可以看到:

var qcloud = require('./lib/index'); App({ onLaunch: function () { // 展示本地存儲能力 // qcloud.setLoginUrl(config.url + 'getwxinfo'); // qcloud.setLoginUrl(config.url + 'login'); }, globalData: { userInfo: null } }) 

這里注釋掉了 qcloud.setLoginUrl 。打開定義為 qcloud 的 ./lib/index 文件,代碼包括:

var constants = require('./constants'); var login = require('./login'); var Session = require('./session'); var request = require('./request'); var Tunnel = require('./tunnel'); var SocketTunnel = require('./socketTunnel'); var exports = module.exports = { login: login.login, setLoginUrl: login.setLoginUrl, LoginError: login.LoginError, clearSession: Session.clear, request: request.request, RequestError: request.RequestError, Tunnel: Tunnel, SocketTunnel: SocketTunnel, }; // 導出錯誤類型碼 Object.keys(constants).forEach(function (key) { if (key.indexOf('ERR_') === 0) { exports[key] = constants[key]; } }); 

從上面的代碼中,我們可以看到,除了 setLoginUrl ,qcloud 還有 login、clearSession、request、Tunnel、SocketTunnel、LoginError、RequestError 等方法。

login 函數需要在 app.js 中使用 setLoginUrl 設置與后台交互的 url 。設置 url 后,我們需要了解 login 是如何工作的,從而確認后台如何與 login 交互。

login.js 分析

打開 wxlite/lib/login.js 文件,我們可以看到模塊輸出三個方法:

module.exports = { LoginError: LoginError, login: login, setLoginUrl: setLoginUrl, }; 

setLoginUrl

我們先看 setLoginUrl ,這個函數的功能非常簡單,設置后台登錄 url :

var setLoginUrl = function (loginUrl) { defaultOptions.loginUrl = loginUrl; }; 

代碼中,只是將 defaultOptions 的 loginUrl 設置為輸入的 loginUrl 。這里 defaultOptions 是 login.js 定義的全局變量,它的初始化設置為:

var defaultOptions = { method: 'GET', success: noop, fail: noop, loginUrl: null, }; 

這里的 noop 為:

var noop = function noop() {}; 

LoginError

LoginError 是處理登錄錯誤的,其代碼為:

var LoginError = (function () { function LoginError(type, message) { Error.call(this, message); this.type = type; this.message = message; } LoginError.prototype = new Error(); LoginError.prototype.constructor = LoginError; return LoginError; })(); 

login

流程

login 方法用於實現登錄功能,代碼流程為:

 
login流程.jpg

這里,實際的動作為 獲取 session,檢查 session並進行相應操作。

獲取 session 使用的 Session.get() 方法,打開 Session 對應的 session.js 文件,get() 方法的功能為從微信緩存中獲取 'weapp_session_F2C224D4-2BCE-4C64-AF9F-A6D872000D1A' 對應的值。這個值是在第一運行 doLogin 時設置的。那么,我們接下來分析 doLogin 。

doLogin

doLogin 的代碼為:

var doLogin = () => getWxLoginResult(function (wxLoginError, wxLoginResult) { if (wxLoginError) { options.fail(wxLoginError); return; } var userInfo = wxLoginResult.userInfo; // 構造請求頭,包含 code、encryptedData 和 iv var code = wxLoginResult.code; var encryptedData = wxLoginResult.encryptedData; var iv = wxLoginResult.iv; var header = {}; header[constants.WX_HEADER_CODE] = code; header[constants.WX_HEADER_ENCRYPTED_DATA] = encryptedData; header[constants.WX_HEADER_IV] = iv; // 請求服務器登錄地址,獲得會話信息 wx.request({ url: options.loginUrl, header: header, method: options.method, data: options.data, success: function (result) { var data = result.data; // 成功地響應會話信息 if (data && data.code === 0 && data.data.skey) { var res = data.data if (res.userinfo) { Session.set(res.skey); options.success(res.iminfo); } else { var errorMessage = '登錄失敗(' + data.error + '):' + (data.message || '未知錯誤'); var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, errorMessage); options.fail(noSessionError); } // 沒有正確響應會話信息 } else { var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, JSON.stringify(data)); options.fail(noSessionError); } }, // 響應錯誤 fail: function (loginResponseError) { var error = new LoginError(constants.ERR_LOGIN_FAILED, '登錄失敗,可能是網絡錯誤或者服務器發生異常'); options.fail(error); }, }); }); 

dologin 實行的是 getWxLoginResult 方法,這里定義的是方法成功或失敗的回調方法。getWxLoginResult 的定義為:

var getWxLoginResult = function getLoginCode(callback) { wx.login({ success: function (loginResult) { wx.getUserInfo({ success: function (userResult) { console.log('[userResult]', userResult); callback(null, { code: loginResult.code, encryptedData: userResult.encryptedData, iv: userResult.iv, userInfo: userResult.userInfo, }); }, fail: function (userError) { console.log('[userError]', userError); var error = new LoginError(constants.ERR_WX_GET_USER_INFO, '獲取微信用戶信息失敗,請檢查網絡狀態'); error.detail = userError; callback(error, null); }, }); }, fail: function (loginError) { console.log('[loginError]', loginError); var error = new LoginError(constants.ERR_WX_LOGIN_FAILED, '微信登錄失敗,請檢查網絡狀態'); error.detail = loginError; callback(error, null); }, }); }; 

結合這兩個方法,可以分析 doLogin 的邏輯為:

 
doLogin流程.jpg

從這里我們可以看到,后台處理登錄請求的視圖將接收的 code、encryptedData 和 iv 放在請求頭中,后台返回的響應至少需要返回 code、data 兩個元素,如果成功執行,code 需要設為 0 ,data 中至少需要包含 skey 、userinfo 和 iminfo 三項內容。

用法

我們已經了解了 login 的基本原理,那么,我們應該怎樣使用 login 呢?

與 login.js 位於同一文件夾的 request.js 提供設置 login 為 ture 的 Request 請求實現先登錄后請求,我們看下這個方法是怎么使用 login.js 的。

request 中,如果定義了 login 為 true ,則requirLogin 為 true,請求將執行doRequestWithLogin()。

if (requireLogin) { doRequestWithLogin(); } else { doRequest(); } 

doRequestWithLogin() 定義了登錄后再請求的函數:

function doRequestWithLogin() { loginLib.login({ success: doRequest, fail: callFail }); } 

如果登錄成功,將執行 doRequest() ,失敗則執行 callFail()。

doRequest 執行時首先構造 authHeader 將從微信緩存中獲取的 'weapp_session_F2C224D4-2BCE-4C64-AF9F-A6D872000D1A' 的值寫到 X-WX-Skey 請求頭中進行發送。

問題

整個流程中,如果登錄過之后再次調用 login ,返回 Session.get() 中的 userinfo 。但是在首次登錄成功時或者 request 設置login 為 true 的 request 成功時,Session.set() 設置的是 skey 的值,並沒有 userInfo 的值。是否可以擴展 Session 中的方法來保存和讀取用戶信息。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM