Wie implementiert man die Login-Funktion im Miniprogramm? In diesem Artikel erfahren Sie, wie Sie das Mini-Programm-Login richtig öffnen. Ich hoffe, er hilft Ihnen weiter!
Mini-Programm-Netzwerkkomponente
https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html
RequestTask-Beschreibung
Methode | Beschreibung |
---|---|
RequestTask.abort() | Abbrechen der Anforderungsaufgabe. |
RequestTask.onHeadersReceived(Funktionsrückruf) | HTTP-Antwort-Header-Ereignisse abhören. tritt früher ein als das Anforderungsabschlussereignis. |
RequestTask.offHeadersReceived(Funktionsrückruf) | Abbrechen der Überwachung auf HTTP-Response-Header-Ereignisse. |
RequestTask.onChunkReceived(function callback) | Hören Sie sich das Transfer-Encoding Chunk Received-Ereignis an. Wird ausgelöst, wenn ein neuer Block empfangen wird. |
RequestTask.offChunkReceived(Funktionsrückruf) | Abbrechen des Abhörens des Transfer-Encoding Chunk Received-Ereignisses. |
wx.request(Object object) attribute
Hier werden nur die am häufigsten verwendeten Attribute aufgeführt. Alle Attribute finden Sie unter demLink.
Eigenschaft | Typ | Standardwert | Erforderlich | Beschreibung |
---|---|---|---|---|
URL | Zeichenfolge | Ja | Schnittstellenadresse des Entwicklerservers | |
Daten | Zeichenfolge /object/ArrayBuffer | Nein | Angeforderte Parameter | |
header | Object | Nein | Legen Sie den angeforderten Header fest. Der Referrer kann nicht im Header festgelegt werden.content-type 默认为application/json |
|
Timeout | Nummer | Nein | Timeout in Millisekunden | |
Methode | String | GET | Nein | HTTP-Anfrage Methode |
Erfolg | Funktion | Nein | Die Rückruffunktion für einen erfolgreichen Schnittstellenaufruf. | |
fehlgeschlagene Funktion. | Nein. |
Die Rückruffunktion für einen fehlgeschlagenen Schnittstellenaufruf | ||
Die Rückruffunktion am Ende des Schnittstellenaufrufs (wird unabhängig davon ausgeführt, ob der Aufruf erfolgreich ist oder fehlschlägt), auch wenn es sich um eine abgebrochene Anfrage handelt! |
alle haben die gleiche Ergebnisverarbeitungsmethode: Sie alle haben drei Rückrufattribute: Erfolg, Fehler und Abschluss.
Einführung in das errMsg-Objekt in verschiedenen Situationen der Schnittstellenausführung.
errMsg-Objekt
Beispielcode
let reqTask = wx.request({ url: getApp().globalData.api, success(res) { if (res.errMsg === "request:ok") console.log("res", res); }, fail(err) { // if(err.errMsg.indexOf('request:fail')>-1) console.log('err', err); if (/^request:fail/i.test(err.errMsg)) console.log("err", err); }, complete(res) { console.log("resOrErr", res); }, }); const reqTaskOnHeadersReceived = (headers) => { reqTask.offHeadersReceived(reqTaskOnHeadersReceived); console.log("headers", headers); // 由于请求还未完全结束,所以我们没办法获得请求的状态码,但是我们可以通过返回的requestBody的长度来进行判断。 // 两点说明:1. 两个~~可以把字符串数字快速转化为数字。 // 2. 为什么取小于19,是由于后台返回没有权限的requestBody的时候Content-length为“18”,正常情况下是大于19的。所以具体多少得看一下具体情况。 if (~~headers.header["Content-length"] < 19) reqTask.abort(); }; reqTask.onHeadersReceived(reqTaskOnHeadersReceived);
Mini-Programm-Anmeldeschnittstelle
wx.getUserProfile(Object object)
Benutzerinformationen abrufen. Die Seite generiert ein Klickereignis (zum Beispielbutton
上bindtap
的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回userInfo
。该接口用于替换wx.getUserInfo
, Einzelheiten finden Sie unterAnweisungen zur Anpassung der Benutzerinformationsschnittstelle).
Überprüfen Sie, ob der Anmeldestatus abgelaufen ist. Der Benutzeranmeldestatus Über die wx.login-Schnittstelle erhalten Sie eine gewisse Aktualität. Je länger der Benutzer das Miniprogramm nicht verwendet, desto wahrscheinlicher ist es, dass der Anmeldestatus des Benutzers ungültig wird. Im Gegenteil, wenn der Benutzer das Miniprogramm verwendet hat. Die spezifische Timing-Logik wird von WeChat verwaltet und ist für Entwickler transparent. Entwickler müssen lediglich die Schnittstelle wx.checkSession aufrufen, um zu überprüfen, ob der aktuelle Anmeldestatus des Benutzers gültig ist.
Nach Ablauf des Anmeldestatus kann der Entwickler wx aufrufen .login, um einen neuen Benutzer-Anmeldestatus zu erhalten, bedeutet, dass der aktuelle Sitzungsschlüssel nicht abgelaufen ist, und ein fehlgeschlagener Aufruf bedeutet, dass der Sitzungsschlüssel abgelaufen ist wx.login(Objektobjekt)
Rufen Sie die Schnittstelle auf, um die Benutzeranmeldestatusinformationen (Code) zu erhalten. Die eindeutige Kennung des aktuellen Miniprogramms (openid), die eindeutige Kennung des offenen WeChat-Plattformkontos (unionid, falls vorhanden). Das aktuelle Miniprogramm ist an das offene WeChat-Plattformkonto und den Sitzungsschlüssel (session_key) dieser Anmeldung usw. gebunden. Die Verschlüsselungs- und Entschlüsselungskommunikation muss mithilfe des Sitzungsschlüssels abgeschlossen werden. Weitere Informationen finden Sie unter
// lib/koa2-weixin-auth.js const querystring = require("querystring"); const request = require("request"); const AccessToken = function (data) { if (!(this instanceof AccessToken)) { return new AccessToken(data); } this.data = data; }; /*! * 检查AccessToken是否有效,检查规则为当前时间和过期时间进行对比 * * Examples: * ``` * token.isValid(); * ``` */ AccessToken.prototype.isValid = function () { return ( !!this.data.session_key && new Date().getTime() < this.data.create_at + this.data.expires_in * 1000 ); }; /** * 根据appid和appsecret创建OAuth接口的构造函数 * 如需跨进程跨机器进行操作,access token需要进行全局维护 * 使用使用token的优先级是: * * 1. 使用当前缓存的token对象 * 2. 调用开发传入的获取token的异步方法,获得token之后使用(并缓存它)。 * Examples: * ``` * var OAuth = require('oauth'); * var api = new OAuth('appid', 'secret'); * ``` * @param {String} appid 在公众平台上申请得到的appid * @param {String} appsecret 在公众平台上申请得到的app secret */ const Auth = function (appid, appsecret) { this.appid = appid; this.appsecret = appsecret; this.store = {}; this.getToken = function (openid) { return this.store[openid]; }; this.saveToken = function (openid, token) { this.store[openid] = token; }; }; /** * 获取授权页面的URL地址 * @param {String} redirect 授权后要跳转的地址 * @param {String} state 开发者可提供的数据 * @param {String} scope 作用范围,值为snsapi_userinfo和snsapi_base,前者用于弹出,后者用于跳转 */ Auth.prototype.getAuthorizeURL = function (redirect_uri, scope, state) { return new Promise((resolve, reject) => { const url = "https://open.weixin.qq.com/connect/oauth2/authorize"; let info = { appid: this.appid, redirect_uri: redirect_uri, scope: scope || "snsapi_base", state: state || "", response_type: "code", }; resolve(url + "?" + querystring.stringify(info) + "#wechat_redirect"); }); }; /*! * 处理token,更新过期时间 */ Auth.prototype.processToken = function (data) { data.create_at = new Date().getTime(); // 存储token this.saveToken(data.openid, data); return AccessToken(data); }; /** * 根据授权获取到的code,换取access token和openid * 获取openid之后,可以调用`wechat.API`来获取更多信息 * @param {String} code 授权获取到的code */ Auth.prototype.getAccessToken = function (code) { return new Promise((resolve, reject) => { const url = "https://api.weixin.qq.com/sns/jscode2session"; //由于此框架版本很久没有更新了,此处地址发生了变化,需要修改为以上地址,不然会出现 //41008错误。这也是没有直接使用框架,引用本地使用的原因。 // const url = "https://api.weixin.qq.com/sns/oauth2/access_token"; const info = { appid: this.appid, secret: this.appsecret, js_code: code, grant_type: "authorization_code", }; request.post(url, { form: info }, (err, res, body) => { if (err) { reject(err); } else { const data = JSON.parse(body); resolve(this.processToken(data)); } }); }); }; /** * 根据refresh token,刷新access token,调用getAccessToken后才有效 * @param {String} refreshToken refreshToken */ Auth.prototype.refreshAccessToken = function (refreshToken) { return new Promise((resolve, reject) => { const url = "https://api.weixin.qq.com/sns/oauth2/refresh_token"; var info = { appid: this.appid, grant_type: "refresh_token", refresh_token: refreshToken, }; request.post(url, { form: info }, (err, res, body) => { if (err) { reject(err); } else { const data = JSON.parse(body); resolve(this.processToken(data)); } }); }); }; /** * 根据openid,获取用户信息。 * 当access token无效时,自动通过refresh token获取新的access token。然后再获取用户信息 * @param {Object|String} options 传入openid或者参见Options */ Auth.prototype.getUser = async function (openid) { const data = this.getToken(openid); console.log("getUser", data); if (!data) { var error = new Error( "No token for " + options.openid + ", please authorize first." ); error.name = "NoOAuthTokenError"; throw error; } const token = AccessToken(data); var accessToken; if (token.isValid()) { accessToken = token.data.session_key; } else { var newToken = await this.refreshAccessToken(token.data.refresh_token); accessToken = newToken.data.session_key; } console.log("accessToken", accessToken); return await this._getUser(openid, accessToken); }; Auth.prototype._getUser = function (openid, accessToken, lang) { return new Promise((resolve, reject) => { const url = "https://api.weixin.qq.com/sns/userinfo"; const info = { access_token: accessToken, openid: openid, lang: lang || "zh_CN", }; request.post(url, { form: info }, (err, res, body) => { if (err) { reject(err); } else { resolve(JSON.parse(body)); } }); }); }; /** * 根据code,获取用户信息。 * @param {String} code 授权获取到的code */ Auth.prototype.getUserByCode = async function (code) { const token = await this.getAccessToken(code); return await this.getUser(token.data.openid); }; module.exports = Auth;
// pages/index.js Page({ /** * 页面的初始数据 */ data: {}, // 正确的登录方式 getUserProfile() { // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认 // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 wx.getUserProfile({ desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success: (res) => { let { userInfo, encryptedData, iv } = res; const requestLoginApi = (code) => { // 发起网络请求 wx.request({ url: "http://localhost:3000/user/weixin-login", method: "POST", header: { "content-type": "application/json", }, data: { code, userInfo, encryptedData, iv, }, success(res) { console.log("请求成功", res.data); let token = res.data.data.authorizationToken; wx.setStorageSync("token", token); onUserLogin(token); console.log("authorization", token); }, fail(err) { console.log("请求异常", err); }, }); }; const onUserLogin = (token) => { getApp().globalData.token = token; wx.showToast({ title: "登录成功了", }); }; //必须进行session是否过期检查,不然会出现第一次点击登录,服务器报Illegal Buffer //的错误,但是第二次点击登录正常。 wx.checkSession({ success: (res) => { // session_key 未过期,并且在本生命周期一直有效 console.log("在登陆中"); let token = wx.getStorageSync("token"); if (token) onUserLogin(token); }, fail: (res) => { // session_key已经失效,需要重新执行登录流程 wx.login({ success(res0) { if (res0.code) { requestLoginApi(res0.code); } else { console.log("登录失败!" + res0.errMsg); } }, }); }, }); }, }); }, });
Welche Optimierungen können für den Anmeldecode vorgenommen werden?Für Bei einer Software müssen Sie auf Codeebene die grundlegendsten Aspekte verfolgen (weit mehr als diese, aber machen wir diese zuerst)):
WartbarkeitDie sogenannte „Wartung“ ist nichts anderes als solche Arbeit B. das Ändern von Fehlern, das Ändern von altem Code und das Hinzufügen von neuem Code. Das sogenannte „Code ist leicht zu warten“ bedeutet, dass Code schnell geändert oder hinzugefügt werden kann, ohne das ursprüngliche Codedesign zu zerstören oder neue Fehler einzuführen. Code ist nicht einfach zu warten“ bedeutet, dass das Ändern oder Hinzufügen von Code ein großes Risiko für die Einführung neuer Fehler birgt und lange dauert Code, den ein Computer verstehen kann. „Gute Programmierer schreiben Code, den Menschen verstehen können.“ Ins Chinesische übersetzt: „Jeder Narr kann Code schreiben, den ein Computer verstehen kann.“ Gute Programmierer können Code schreiben, den die Leute verstehen können. „Innerhalb von Google gibt es sogar eine spezielle Zertifizierung namens Readability. Nur Ingenieure, die diese Zertifizierung erhalten haben, sind qualifiziert, die Code-Einreichungen anderer Personen während der Codeüberprüfung zu genehmigen. Man sieht, wie wichtig die Lesbarkeit des Codes ist. Schließlich ist der Code.“ Die Anzahl der Lesevorgänge übersteigt die Anzahl der Schreib- und Ausführungsvorgänge bei weitem. Wir müssen prüfen, ob der Code den Codierungsstandards entspricht, ob die Benennung sinnvoll ist, ob die Kommentare detailliert sind und ob die Funktionslänge angemessen ist Die Modulaufteilung ist klar, ob sie eine hohe Kohäsion und eine geringe Kopplung usw. erfüllt.
Erweiterbarkeit ist auch ein sehr wichtiges Kriterium für die Bewertung der Codequalität. Sie können einige Funktionserweiterungspunkte direkt reservieren Fügen Sie ohne großen Aufwand neuen Funktionscode in die Erweiterungspunkte ein und ändern Sie viel Originalcode, weil Sie eine Funktion hinzufügen möchten
代码的可复用性可以简单地理解为,尽量减少重复代码的编写,复用已有的代码。
那么接下来就来优化一下代码吧:
模块化
可以把登录的代码模块化,代码如下:
// lib/login.js function loginWithCallback(cb) { // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认 // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 wx.getUserProfile({ desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success: (res) => { let { userInfo, encryptedData, iv } = res; const requestLoginApi = (code) => { // 发起网络请求 wx.request({ url: "http://localhost:3000/user/weixin-login", method: "POST", header: { "content-type": "application/json", }, data: { code, userInfo, encryptedData, iv, }, success(res) { console.log("请求成功", res.data); let token = res.data.data.authorizationToken; wx.setStorageSync("token", token); onUserLogin(token); console.log("authorization", token); }, fail(err) { console.log("请求异常", err); }, }); }; const onUserLogin = (token) => { getApp().globalData.token = token; wx.showToast({ title: "登录成功了", }); if (cb && typeof cb == "function") cb(token); }; wx.checkSession({ success: (res) => { // session_key 未过期,并且在本生命周期一直有效 console.log("在登陆中"); let token = wx.getStorageSync("token"); if (token) onUserLogin(token); }, fail: (res) => { // session_key已经失效,需要重新执行登录流程 wx.login({ success(res0) { if (res0.code) { requestLoginApi(res0.code); } else { console.log("登录失败!" + res0.errMsg); } }, }); }, }); }, }); } export default loginWithCallback;
Promise化
回调地狱问题,不利于代码的阅读,所以接下来我们基于Promise进行代码优化。有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise的几个方法简介
{errMsg:"request:ok"...} | |
---|---|
abort | |
Das Backend verwendet NodeJS, Web-Framework KOA-Version ^2.13.4, Routing-Framework @koa/Router-Version ^10.1.1, Framework-Anfrage, Version ^2.88. 2, jsonwebtoken wird zum Verschlüsseln und Entschlüsseln von Token-Informationen verwendet, Version ^8.5.1 |
Methodenname | Beschreibung |
---|---|
Promise.prototype.then | Die Methode gibt ein neues Promise-Objekt zurück, sodass es verkettet geschrieben werden kann. Dieses Design ermöglicht das einfache Umschreiben verschachtelter asynchroner Vorgänge, von der „horizontalen Entwicklung“ von Rückruffunktionen bis zur „Abwärtsentwicklung“. |
Promise.prototype.catch | ist ein Alias von Promise.prototype.then (null, Ablehnung) und wird verwendet, um die Rückruffunktion anzugeben, wenn ein Fehler auftritt. Fehler bei Promise-Objekten haben einen „Blasen“-Charakter und werden rückwärts weitergegeben, bis sie abgefangen werden. Das heißt, der Fehler wird immer von der nächsten Catch-Anweisung abgefangen. Die Methode |
Promise.prototype.finally | gibt einPromise Promise 。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise 是否成功完成后都需要执行的代码提供了一种方式。 |
Promise.all | 这避免了同样的语句需要在then() 和catch() 中各写一次的情况。Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.all 方法接受一个数组作为参数,var p = Promise.all([p1,p2,p3]); p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)p 的状态由 p1、p2、p3 决定,分成两种情况。 (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 |
Promise.race | Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。var p = Promise.race([p1,p2,p3]); 上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。 |
Promise.any | 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。所有子实例都处于rejected状态,总的promise才处于rejected状态。 |
Promise.allSettled | 返回一个在所有给定的promise都已经fulfilled 或rejected 后的promise,并带有一个对象数组,每个对象表示对应的promise结果。相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject zurück. Am Ende des Versprechens wird die angegebene Rückruffunktion ausgeführt, unabhängig davon, ob das Ergebnis erfüllt oder abgelehnt wird. Dies bietet eine Möglichkeit für Code, der ausgeführt werden muss, unabhängig davon, ob dasPromise erfolgreich abgeschlossen wird oder nicht. |
then()
undcatch()
geschrieben werden muss. Die Promise.all-Methode wird verwendet, um mehrere Promise-Instanzen in eine neue Promise-Instanz zu packen. Die Promise.all-Methode akzeptiert ein Array als Parameter,var p = Promise.all([p1,p2,p3]);
p1, p2, p3 sind alle Instanzen des Promise-Objekts. (Die Parameter der Promise.all-Methode müssen keine Arrays sein, sie müssen jedoch über eine Iteratorschnittstelle verfügen und jedes zurückgegebene Mitglied ist eine Promise-Instanz.) Der Zustand von p wird durch p1, p2 und p3 bestimmt und ist in zwei Situationen unterteilt. (1) Erst wenn der Status von p1, p2 und p3 erfüllt ist, wird der Status von p erfüllt. Zu diesem Zeitpunkt bilden die Rückgabewerte von p1, p2 und p3 ein Array und werden an übergeben Callback-Funktion von p. (2) Solange einer von p1, p2 und p3 abgelehnt wird, wird der Status von p abgelehnt. Zu diesem Zeitpunkt wird der Rückgabewert der ersten abgelehnten Instanz an die Rückruffunktion von p übergeben.
Promise.raceDie Promise.race-Methode verpackt auch mehrere Promise-Instanzen in eine neue Promise-Instanz.var p = Promise.race([p1,p2,p3]);
Solange im obigen Code eine Instanz von p1, p2 und p3 zuerst den Zustand ändert, ändert sich der Zustand von p entsprechend ändern. Der Rückgabewert der Promise-Instanz, die sich zuerst geändert hat, wird an den Rückgabewert von p übergeben.
Promise.any empfängt ein iterierbares Promise-Objekt, und solange eines der Versprechen erfolgreich ist, wird das erfolgreiche Versprechen zurückgegeben. Alle Unterinstanzen befinden sich im Status „Abgelehnt“ und das gesamte Versprechen befindet sich im Status „Abgelehnt“.
Promise.allSettledGibt ein Versprechen zurück, nachdem alle gegebenen Versprechenerfüllt
oderabgelehnt
wurden, mit einem Array von Objekten, wobei jedes Objekt das entsprechende Ergebnis des Versprechens darstellt. Im Gegensatz dazu eignet sichPromise.all()
besser für Abhängigkeiten voneinander oder endet sofort, wenn einer von ihnenablehnt
.小程序API接口Promise化并且把需要登录的调用接口模块化
npm install --save miniprogram-api-promise
2、在微信开发者工具右方详情中勾选使用npm模块,并在菜单栏工具中点击构建npm。
3、初始化代码。
// app.js import {promisifyAll} from 'miniprogram-api-promise' import login from "../lib/login"; const wxp ={} promisifyAll(wx,wxp) // 需要token的请求统一处理登录和设置header,并且处理错误信息 wxp.requestNeedLogin = async function (args) { let token = wx.getStorageSync("token"); if (!token) { token = await loginWithPromise(); } if (!args.header) args.header = {}; args.header["Authorization"] = `Bearer ${token}`; return wxp.request(args).catch(console.error); }; // app.js App({ wxp:wxp, });
4、改写login.js代码
// lib/login.js function login() { return new Promise((resolve, reject) => { // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认 // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 wx.getUserProfile({ desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success:async (res0) => { let { userInfo, encryptedData, iv } = res0; const app = getApp(); try { app.wxp.checkSession(); } catch (err) { reject(err); } let token = wx.getStorageSync("token"); if (!token) { let res1 = await app.wxp.login().catch(err => reject(err)); let code = res1.code; let res = await app.wxp.request({ url: "http://localhost:3000/user/weixin-login", method: "POST", header: { "content-type": "application/json", }, data: { code, userInfo, encryptedData, iv, } }).catch(err => reject(err)); token = res.data.data.authorizationToken; wx.setStorageSync("token", token); app.globalData.token = token; wx.showToast({ title: "登录成功了", }); resolve(token); } }, }); }) } export default login;
5、调用代码
需要登录的请求调用
// pages/index.js Page({ /** * 页面的初始数据 */ data: {}, request1() { getApp().wxp.requestNeedLogin({ url: "http://localhost:3000/user/home?name=andying", }).then(console.log) }, request2() { getApp().wxp.requestNeedLogin({ url: "http://localhost:3000/user/home?name=eva", }).then(console.log) }, });
【相关学习推荐:小程序开发教程】
Das obige ist der detaillierte Inhalt vonEine kurze Analyse, wie die Login-Funktion in Miniprogrammen implementiert wird. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!