《微信小程序》第三章:Request封装

系列文章

《微信小程序》https://blog.csdn.net/sen_shan/category_13069009.html

第二章:第一个程序https://blog.csdn.net/sen_shan/article/details/153685143?spm=1001.2014.3001.5501

文章目录

目录

系列文章

文章目录

前言

Toast封装

Request封装

后续


前言

本文介绍了微信小程序开发中的两个核心工具封装:Toast提示和Request请求。

Toast封装通过统一配置解决了提示时长不一致、图标类型错误和错误提示样式问题;

Request封装则构建了一个类型安全的网络请求层,统一处理域名配置、超时设置、Header管理、Token携带、错误提示等功能,并内置了401登录失效自动处理机制。文章重点展示了如何通过TypeScript类型系统提升开发体验,使业务代码只需关注核心逻辑,同时保持对请求参数和返回数据的类型安全。这些封装旨在减少重复代码,提高开发效率和代码可维护性。

Toast封装

在 WechatMiniApp 项目下新建 src/utils/toast.ts

javascript 复制代码
type ToastIcon = 'success' | 'loading' | 'none' | undefined

const DEFAULT_OPTS = {
  duration: 4000, // 统一 4 秒
  icon: 'success' as ToastIcon,
}

export function toast(title: string, icon?: ToastIcon, duration = DEFAULT_OPTS.duration) {
  uni.showToast({ title, icon: icon ?? DEFAULT_OPTS.icon, duration })
}

// 可选:错误专用(红色图标/更长)
export function toastError(title: string) {
  toast(title, 'none', 5000)
}

解决调用 uni.showToast 时出现的 3 类问题:

  1. 提示时长不统一(2 s / 3 s / 5 s 随意写)。

  2. 图标类型写错(success 拼成 succes、loading 写成 load)。

  3. 错误提示样式与正常提示无区分,用户感知弱。

Request封装

在 WechatMiniApp 项目下新建 src/utils/reques.ts

javascript 复制代码
// 顶部引入
import { toastError } from './toast'
/* 配置 ------------------------------------------------------------------ */
const BASE_URL = 'http://127.0.0.1:8080' // 替换你的后端域名
const TIMEOUT = 10_000 // ms

/* 类型定义 --------------------------------------------------------------- */
export type Method = 'GET' | 'POST' | 'PUT' | 'DELETE'

export interface RequestOptions<T = any> {
  url: string
  method?: Method
  data?: T
  headers?: Record<string, string>
  noToken?: boolean // 跳过 token
  noBaseURL?: boolean // 跳过 baseURL
  timeout?: number
}

// 后端统一返回格式(按你的实际改)
export interface HttpResponse<T = any> {
  status_code: number
  message: string
  data: T
}

/* 核心请求 --------------------------------------------------------------- */
export function request<R = any, T = any>(options: RequestOptions<T>): Promise<R> {
  return new Promise((resolve, reject) => {
    const token = uni.getStorageSync('token')
    const header: AnyObject = {
      'Content-Type': 'application/json; charset=utf-8',
	  'x-api-key': 'ae139f8f6a8efc2a74a1e852c5cdaa43', // ← 新增这一行
      ...options.headers,
    }

    if (!options.noToken && token) header.Authorization = `Bearer ${token}`

    uni.request({
      url: options.noBaseURL ? options.url : `${BASE_URL}${options.url}`,
      method: options.method || 'GET',
      data: options.data || {},
      header,
      timeout: options.timeout || TIMEOUT,
      success: (res) => handleSuccess<R>(res, resolve, reject),
      fail: (err) => handleFail(err, reject),
    })
  })
}

/* 成功处理 --------------------------------------------------------------- */
function handleSuccess<R>(
  res: UniApp.RequestSuccessCallbackResult,
  resolve: (value: R) => void,
  reject: (reason?: any) => void,
) {
  const { statusCode, data } = res
  // 成功区间
  if (statusCode >= 200 && statusCode < 300) {
    const resp = data as HttpResponse<R>
	console.log(resp)
    // 后端业务码自定义,这里以 0 为成功
    if (resp.status_code === 200) resolve(resp.data)
    else {
      toastError( resp.message)
      reject(resp)
    }
    return
  }
  // 401 登录态失效
  // if (statusCode === 401) {
  //   uni.clearStorageSync()
  //   uni.reLaunch({ url: '/pages/login/index' })
  //   reject(new Error('登录已过期'))
  //   return
  // }
  if (statusCode === 401) {
    const detail = (data as any)?.detail || '登录已过期'
    // uni.showToast({ title: detail, 
  				// 	icon: 'none',
  				// 	duration: 5000, // 5 秒
  				// })   // 需要提示就留,不需要就删
  	toastError( detail)   // 需要提示就留,不需要就删
    uni.clearStorageSync()
    uni.reLaunch({ url: '/pages/login/index' })
    reject(new Error(detail))
    return
  }
  if (statusCode !== 401 && statusCode >= 300 ) {
    const detail = (data as any)?.detail || '登录已过期'
    // uni.showToast({ title: detail, 
				// 	icon: 'none',
				// 	duration: 5000, // 5 秒
				// })   // 需要提示就留,不需要就删
	toastError( detail)   // 需要提示就留,不需要就删
    uni.clearStorageSync()
    // uni.reLaunch({ url: '/pages/login/index' })
    reject(new Error(detail))
    return
  }
  // 其他 HTTP 错误
  //uni.showToast({ title: `请求失败(${statusCode})`, icon: 'none' })
  toastError( `请求失败(${statusCode})`)
  reject(new Error(JSON.stringify(data)))
}

/* 失败处理 --------------------------------------------------------------- */
function handleFail(err: any, reject: (reason?: any) => void) {
  // uni.showToast({ title: '网络异常', icon: 'none' })
  toastError( `网络异常`)
  reject(err)
}

/* 上传文件 --------------------------------------------------------------- */
export function upload<R = any>(
  url: string,
  filePath: string,
  formData?: Record<string, any>,
  timeout = 30_000,
): Promise<R> {
  const token = uni.getStorageSync('token')
  return new Promise((resolve, reject) => {
    uni.uploadFile({
      url: `${BASE_URL}${url}`,
      filePath,
      name: 'file',
      formData,
      header: { Authorization: `Bearer ${token}` },
      timeout,
      success: (res) => {
        // uploadFile 返回字符串,先 parse
        const data: HttpResponse<R> = JSON.parse(res.data)
        if (data.status_code === 0) resolve(data.data)
        else {
          uni.showToast({ title: data.message || '上传失败', icon: 'none' })
          reject(data)
        }
      },
      fail: reject,
    })
  })
}

/* 导出别名(可选)-------------------------------------------------------- */
export const http = { get: <R = any>(url: string, params?: any) => request<R>({ url, data: params }),
                     post: <R = any>(url: string, data?: any) => request<R>({ url, method: 'POST', data }),
                     put: <R = any>(url: string, data?: any) => request<R>({ url, method: 'PUT', data }),
                     delete: <R = any>(url: string) => request<R>({ url, method: 'DELETE' }) }

目的

  1. 在 uni-app 项目里提供"一处配置、处处可用"的网络请求层,统一接管:

域名、超时、header、token、错误提示;

业务状态码与 HTTP 状态码的双重校验;

401 登录失效的自动清理与跳登录页;

所有异常统一使用 toastError 弹出红色错误提示,避免每个接口重复写 uni.showToast 。

  1. 通过 TypeScript 泛型与类型定义,让调用者在编译期即可获得:

请求参数提示 ( RequestOptions<T> );

返回数据结构提示 ( HttpResponse<R> → 直接拿到 R )。

  1. 额外内置文件上传常用 get/post/put/delete 别名,业务层基本不再需要自己拼 uni.request 。

核心功能

自动携带 x-api-key 与 Authorization (Bearer token),其中x-api-key自定义字段,按需增加

支持开关 noToken 、 noBaseURL ,方便调第三方开放接口。

200--300 之间且业务码 status_code === 200 才走 resolve ,否则弹错并 reject 。

401 场景:清缓存 → 跳登录页 → 返回 reject ,防止死循环。

网络层或 HTTP 异常统一 toastError 提示,业务只需 .catch() 即可,无需再写提示逻辑。

把"发请求-拼头-验码-弹错-登出"全部收拢,业务代码只关心 url 和 data 。

后续

1.Request应用案例

2.参数封装

相关推荐
sen_shan4 小时前
《微信小程序》第一章:开发前准备与配置
微信小程序·小程序
游戏开发爱好者85 小时前
HTTPS 内容抓取实战 能抓到什么、怎么抓、不可解密时如何定位(面向开发与 iOS 真机排查)
android·网络协议·ios·小程序·https·uni-app·iphone
stark张宇10 小时前
超越 Hello World:深入小程序 Hybrid 初衷、安全配置与上线全链路
nginx·微信小程序·php
菜鸟una12 小时前
【瀑布流大全】分析原理及实现方式(微信小程序和网页都适用)
前端·css·vue.js·微信小程序·小程序·typescript
2501_9151063214 小时前
iOS 混淆与 IPA 加固全流程,多工具组合实现无源码混淆、源码防护与可审计流水线(iOS 混淆|IPA 加固|无源码加固|App 防反编译)
android·ios·小程序·https·uni-app·iphone·webview
游戏开发爱好者814 小时前
用多工具组合把 iOS 混淆做成可复用的工程能力(iOS混淆 IPA加固 无源码混淆 Ipa Guard)
android·ios·小程序·https·uni-app·iphone·webview
lypzcgf16 小时前
商城小程序数据库表结构文档
数据库·小程序·电商
2501_9159214316 小时前
掌握 iOS 26 App 性能监控,从监测到优化的多工具组合流程
android·macos·ios·小程序·uni-app·cocoa·iphone
知识分享小能手16 小时前
uni-app 入门学习教程,从入门到精通, uni-app常用API的详细语法知识点(上)(5)
前端·javascript·vue.js·学习·微信小程序·小程序·uni-app