Files
wechat_wc/src/utils/request.ts
T
broccoli b19a56003f 添加登录和后端校验
完成后端设计(未在本仓库体现),通过安全的手段完成了登录鉴权
2026-08-06 16:27:36 +08:00

127 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Taro from '@tarojs/taro'
/**
* 后端接口请求封装
* 后端统一响应:{ code, message, data }code === 0 表示成功
* token 通过 Authorization: Bearer <token> 传入
*/
export const BASE_URL = 'https://wxbackend.tokenleaping.com'
const TOKEN_KEY = 'smart_access_token'
/** 安全隐藏 loading:没有活动 loading 时调用 hideLoading 会抛 hideToast:fail,这里吞掉 */
export function safeHideLoading(): void {
try { Taro.hideLoading() } catch { /* ignore */ }
}
// ---------- token 存取 ----------
export function getToken(): string {
try {
return Taro.getStorageSync(TOKEN_KEY) || ''
} catch {
return ''
}
}
export function setToken(token: string): void {
Taro.setStorageSync(TOKEN_KEY, token)
}
export function clearToken(): void {
try {
Taro.removeStorageSync(TOKEN_KEY)
} catch {
/* ignore */
}
}
// ---------- 类型 ----------
interface ApiResponse<T = unknown> {
code: number
message: string
data: T
}
export interface RequestOptions {
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
data?: unknown
/** 额外请求头 */
headers?: Record<string, string>
/** 是否自动携带 token,默认 true */
auth?: boolean
}
// ---------- request ----------
/**
* 发请求。成功(code===0)返回 data;业务失败抛 Error(message)HTTP 401 清 token 并抛错。
* 调用方可自行 catch 或用 onUnauthorized 钩子统一处理跳登录。
*/
export async function request<T = unknown>(
path: string,
options: RequestOptions = {},
): Promise<T> {
const { method = 'GET', data, headers = {}, auth = true } = options
const token = getToken()
const finalHeaders: Record<string, string> = {
'Content-Type': 'application/json',
...(auth && token ? { Authorization: `Bearer ${token}` } : {}),
...headers,
}
let res: Taro.request.SuccessCallbackResult
try {
res = await Taro.request({
url: `${BASE_URL}${path}`,
method,
data,
header: finalHeaders,
})
} catch (e) {
// 网络层异常
throw new Error('网络异常,请稍后重试')
}
// HTTP 未授权:清 token,交回调处理
if (res.statusCode === 401) {
clearToken()
onUnauthorized?.()
throw new Error('登录已过期,请重新登录')
}
const body = res.data as ApiResponse<T>
// 后端业务码非 0:当作错误抛出
if (body && typeof body.code === 'number' && body.code !== 0) {
throw new Error(body.message || '请求失败')
}
return (body && typeof body.code === 'number' ? body.data : body) as T
}
/** 401 钩子,由业务方注册(如跳转登录页) */
export let onUnauthorized: (() => void) | null = null
export function setOnUnauthorized(handler: () => void): void {
onUnauthorized = handler
}
// ---------- 便捷方法 ----------
export const http = {
get: <T = unknown>(path: string, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'GET' }),
post: <T = unknown>(path: string, data?: unknown, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'POST', data }),
put: <T = unknown>(path: string, data?: unknown, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'PUT', data }),
patch: <T = unknown>(path: string, data?: unknown, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'PATCH', data }),
del: <T = unknown>(path: string, options?: RequestOptions) =>
request<T>(path, { ...options, method: 'DELETE' }),
}
export default http