前端底层初步搭建(SDK)
新建 client/miniprogram/service/sdk.ts
文件,来初步搭建一下我们前端的底层公共设施。
定义一个 SDK
na
mespace
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 }), ) ) } }, 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 = '' 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 }) } }