您的位置:首页 > 移动开发 > 微信开发

Go+gRPC-Gateway(V2) 微服务实战,小程序登录鉴权服务(六):客户端基础库 TS 实战

2021-04-21 12:32 661 查看

小程序登录鉴权服务,客户端底层 SDK,登录鉴权、业务请求、鉴权重试模块 Typescript 实战。

系列

  1. 云原生 API 网关,gRPC-Gateway V2 初探
  2. Go + gRPC-Gateway(V2) 构建微服务实战系列,小程序登录鉴权服务:第一篇
  3. Go + gRPC-Gateway(V2) 构建微服务实战系列,小程序登录鉴权服务:第二篇
  4. Go + gRPC-Gateway(V2) 构建微服务实战系列,小程序登录鉴权服务(三):RSA(RS512) 签名 JWT
  5. Go+gRPC-Gateway(V2) 微服务实战,小程序登录鉴权服务(四):自动生成 API TS 类型
  6. Go+gRPC-Gateway(V2) 微服务实战,小程序登录鉴权服务(五):鉴权 gRPC-Interceptor 拦截器实战

Demo: go-grpc-gateway-v2-microservice

前端底层初步搭建(SDK)

新建

client/miniprogram/service/sdk.ts
文件,来初步搭建一下我们前端的底层公共设施。

定义一个
SDK
namespace

export namespace SDK {

}

定义相关常量 & Interface

const serverAddr = 'http://localhost:8080'
const AUTH_ERR= 'AUTH_ERR'
const authData = {
token: '',
expiryMs: 0
}
interface RequestOption<REQ, RES> {
method: 'GET'|'PUT'|'POST'|'DELETE'
path: string
data: REQ
respMarshaller: (r: object)=>RES
}
interface AuthOption {
attachAuthHeader: boolean
retryOnAuthError: boolean
}

这里主要根据当前需求,做了如下事情:

  • 抽出服务器地址
    serverAddr
  • 定义一个授权失败
    401
    ❌常量
  • token
    相关暂时存到内存中
  • 定义客户端
    wx.request
    所必须的参数类型
  • 控制授权请求相关逻辑(是否附加
    Auth Header
    & 重试等)

wx.login
改写成
Promise
形式

export function wxLogin(): Promise<WechatMiniprogram.LoginSuccessCallbackResult> {
return new Promise((resolve, reject) => {
wx.login({
success: resolve,
fail: reject,
})
})
}

请求公共逻辑
wx.request
编写

export function sendRequest<REQ, RES>(o: RequestOption<REQ, RES>, a: AuthOption): Promise<RES> {
const authOpt = a || {
attachAuthHeader: true,
}
return new Promise((resolve, reject) => {
const header: Record<string, any> = {}
if (authOpt.attachAuthHeader) {
if (authData.token && authData.expiryMs >= Date.now()) {
header.authorization = 'Bearer '+ authData.token
} else {
reject(AUTH_ERR)
return
}
}
wx.request({
url: serverAddr + o.path,
method: o.method,
data: o.data,
header,
success: res => {
if(res.statusCode === 401) {
reject(AUTH_ERR)
} else if (res.statusCode >= 400) {
reject(res)
} else {
resolve(
o.respMarshaller(
camelcaseKeys(res.data as object, { deep: true }),
)
)
}

56c
},
fail: reject
})
})
}

登录模块(
login
)编写

export async function login() {
if (authData.token && authData.expiryMs >= Date.now()) {
return
}
const wxResp = await wxLogin()
const reqTimeMs = Date.now()
const resp = await sendRequest<auth.v1.ILoginRequest, auth.v1.ILoginResponse>({
method: "POST",
path: "/v1/auth/login",
data: {
code: wxResp.code,
},
respMarshaller: auth.v1.LoginResponse.fromObject
}, {
attachAuthHeader: false,
retryOnAuthError: false,
})
authData.token = resp.accessToken!
authData.expiryMs = reqTimeMs + resp.expiresIn! * 1000
}

业务请求自动重试模块编写

export async function sendRequestWithAuthRetry<REQ, RES>(o: RequestOption<REQ, RES>, a?: AuthOption): Promise<RES> {
const authOpt = a || {
attachAuthHeader: true,
retryOnAuthError: true,
}
try {
await login()
return sendRequest(o, authOpt)
} catch(err) {
if(err === AUTH_ERR && authOpt.retryOnAuthError) {
authData.token = ''

ad8
authData.expiryMs = 0
return sendRequestWithAuthRetry(o, {
attachAuthHeader: authOpt.attachAuthHeader,
retryOnAuthError: false
})
} else {
throw err
}
}
}

Todo Service

客户端具体服务层,这里是

Todo
这个服务。

我们新建一个文件控制客户端相关逻辑:

client/miniprogram/service/todo.ts

创建一个
Todo

export namespace TodoService {
export function CreateTodo(req: todo.v1.ICreateTodoRequest): Promise<todo.v1.ICreateTodoResponse>{
return SDK.sendRequestWithAuthRetry({
method: "POST",
path: "/v1/todo",
data: req,
respMarshaller: todo.v1.CreateTodoResponse.fromObject
})
}
}

低层弄好后,上层堆业务就爽很多了。

Refs

我是为少
微信:uuhells123
公众号:黑客下午茶
加我微信(互相学习交流),关注公众号(获取更多学习资料~)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐