サンプル コード
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);
ログイン後にコピー
ミニ プログラム ログイン インターフェイス
##wx 。 getUserProfile(Object object)#ユーザー情報を取得します。これは、ページ上でクリック イベントが生成された後にのみ呼び出すことができます (たとえば、
button の bindtap
のコールバック内)。リクエストごとに認証ウィンドウがポップアップ表示され、 userInfo
は、ユーザーが同意した後に返されます。このインターフェイスは、wx.getUserInfo
を置き換えるために使用されます。詳細については、ユーザー情報インターフェイスの調整手順
を参照してください。
- wx.checkSession(Object object)
ログイン状態の有効期限が切れているかどうかを確認します。 wx.login インターフェースを通じて取得されるユーザーのログインステータスには、一定の適時性があります。ユーザーがミニ プログラムを使用していない期間が長いほど、ユーザーのログイン ステータスが無効になる可能性が高くなります。一方、ユーザーがミニプログラムを使用している場合、ユーザーのログイン状態は常に有効のままです。特定のタイミング ロジックは WeChat によって維持され、開発者には透過的です。開発者は、wx.checkSession インターフェイスを呼び出して、現在のユーザーのログイン ステータスが有効かどうかを確認するだけで済みます。
ログイン状態の有効期限が切れた後、開発者は wx.login を呼び出して新しいユーザーのログイン状態を取得できます。呼び出しが成功した場合は、現在の session_key の有効期限が切れていないことを示し、呼び出しが失敗した場合は、session_key の有効期限が切れたことを示します。その他の利用方法については、
ミニプログラムログインをご覧ください。
- wx.login(Object object)
インターフェースを呼び出して、ログイン資格情報 (コード) を取得します。ユーザーのログイン ステータス情報はバウチャーを通じて交換されます。これには、現在のミニ プログラムでのユーザーの一意の ID (openid)、WeChat オープン プラットフォーム アカウントでの一意の ID (現在のミニ プログラムが WeChat オープン プラットフォームにバインドされている場合は、unionid) が含まれます。アカウント)とこのログイン、セッションキー(session_key)など。ユーザーデータ通信の暗号化と復号化はセッションキーに依存します。その他の利用方法については、
ミニプログラムログインをご覧ください。
#バックエンド ログイン インターフェイス コードの実装バックエンドは、NodeJS、Web フレームワーク KOA バージョン ^2.13 を使用します。 4、ルーティング フレームワーク @koa/router バージョン ^10.1.1、フレームワーク リクエスト、バージョン ^2.88.2、トークン情報の暗号化と復号化に jsonwebtoken が使用されます、バージョン ^8.5.1
// app.js
const Koa = require("koa");
const Router = require("@koa/router");
const WeixinAuth = require("./lib/koa2-weixin-auth");
const jsonwebtoken = require("jsonwebtoken");
const app = new Koa();
// 小程序机票信息
const miniProgramAppId = "*********";
const miniProgramAppSecret = "***********";
const weixinAuth = new WeixinAuth(miniProgramAppId, miniProgramAppSecret);
const JWT_SECRET = "JWTSECRET";
// 路由中间件需要安装@koa/router
// 开启一个带群组的路由
const router = new Router({
prefix: "/user",
});
// 这是正规的登陆方法
// 添加一个参数,sessionKeyIsValid,代表sessionKey是否还有效
router.post("/weixin-login", async (ctx) => {
let { code, userInfo, encryptedData, iv, sessionKeyIsValid } =
ctx.request.body;
// 解析openid
const token = await weixinAuth.getAccessToken(code);
userInfo.openid = token.data.openid;
// 这里可以自己进行处理,比方说记录到数据库,处理token等
let authorizationToken = jsonwebtoken.sign(
{ name: userInfo.nickName },
JWT_SECRET,
{ expiresIn: "1d" }
);
Object.assign(userInfo, { authorizationToken });
ctx.status = 200;
ctx.body = {
code: 200,
msg: "ok",
data: userInfo,
};
});
ログイン後にコピー
// 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.wxml-->
<view class="page-section">
<text class="page-section__title">微信登录</text>
<view class="btn-area">
<button bindtap="getUserProfile" type="primary">登录</button>
</view>
</view>
ログイン後にコピー
// 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);
}
},
});
},
});
},
});
},
});
ログイン後にコピー
ログインコードに対してどのような最適化が可能ですか? ソフトウェアの場合、コード レベルで最も基本的な側面を追求する必要があります (これらよりもはるかに多くの側面がありますが、最初にこれらの側面をしっかりと理解しましょう):
Maintainability (保守性) 那么接下来就来优化一下代码吧:
模块化
可以把登录的代码模块化,代码如下:
// 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的几个方法简介
#メソッド名 | 説明 |
##Promise.prototype.then このメソッドは新しい Promise オブジェクトを返すため、連鎖的に記述することができます。この設計により、コールバック関数の「水平開発」から「下方開発」まで、ネストされた非同期操作を簡単に書き直すことができます。 | |
Promise.prototype.catch は、Promise.prototype.then(null、rejection) のエイリアスで、エラーがいつ発生するかを指定するために使用されます。コールバックが発生します。 Promise オブジェクトのエラーには「バブル」の性質があり、検出されるまで逆方向に伝播します。つまり、エラーは常に次の catch ステートメントによってキャッチされます。 | |
Promise.prototype.finally メソッドは | Promise を返します。 Promise の終了時には、結果が満たされたか拒否されたかに関係なく、指定されたコールバック関数が実行されます。これにより、 Promise が正常に完了するかどうかに関係なく、コードを実行する必要がある方法が提供されます。
|
#Promise.allこれにより、 | then()## で同じステートメントを使用する必要がなくなります。 # と catch() はそれぞれ 1 回ずつ書き込まれます。 Promise.all メソッドは、複数の Promise インスタンスを新しい Promise インスタンスにラップするために使用されます。 Promise.all メソッドは配列をパラメーターとして受け入れます。 var p = Promise.all([p1,p2,p3]);p1、p2、p3 はすべて Promise オブジェクトのインスタンスです。 (Promise.all メソッドのパラメーターは必ずしも配列である必要はありませんが、イテレーター インターフェイスが必要であり、返される各メンバーは Promise インスタンスです。) p の状態は、p1、p2、および p3 によって決定され、次のように分割されます。 2つの状況。 (1) p1、p2、p3 の状態がすべて満たされた場合にのみ、p の状態が満たされます このとき、p1、p2、p3 の戻り値は配列を形成し、 pのコールバック関数。 (2) p1、p2、p3 のいずれかが拒否されると、p の状態は拒否されますが、このとき最初に拒否されたインスタンスの戻り値が p のコールバック関数に渡されます。
| Promise.race
Promise.race メソッドは、複数の Promise インスタンスを新しい Promise インスタンスにラップします。 var p = Promise.race([p1,p2,p3]);上記のコードでは、p1、p2、p3 のうちの 1 つのインスタンスが最初に状態を変更する限り、p の状態が変更されます。それに応じて。 pの戻り値には、最初に変更されたPromiseインスタンスの戻り値が渡されます。 |
| Promise.any
Promise 反復可能オブジェクトを受け取り、Promise の 1 つが成功する限り、成功した Promise が返されます。すべてのサブインスタンスは拒否された状態になり、Promise 全体も拒否された状態になります。 | | Promise.allSettled
指定されたすべての Promise が fulfilled または | rejected# された場合に値を返します。 ## の後の Promise にはオブジェクトの配列が伴い、各オブジェクトは対応する Promise の結果を表します。対照的に、Promise.all() は、相互の依存関係、またはいずれかの依存関係が reject になったときにすぐに終了する場合により適しています。
小程序API接口Promise化并且把需要登录的调用接口模块化 1、安装插件。请先查看npm支持文档。 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、调用代码 <view class="container page-head">
<text class="page-section__title">需要登录的请求调用</text>
<view class="btn-area">
<button bindtap="request1" type="primary">请求1</button>
<button bindtap="request2" type="primary">请求2</button>
</view>
</view> ログイン後にコピー // 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)
},
}); ログイン後にコピー 【相关学习推荐:小程序开发教程】
|
以上がミニプログラムでログイン機能を実装する方法の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
-
2023-04-26 17:59:18
-
2023-04-26 17:47:48
-
2023-04-26 17:41:42
-
2023-04-26 17:37:05
-
2023-04-26 17:31:25
-
2023-04-26 17:27:32
-
2023-04-25 19:57:58
-
2023-04-25 19:53:11
-
2023-04-25 19:49:11
-
2023-04-25 19:41:54