mini-rpc-小程序RPC开发规范.md 8.2 KB


description: "小程序RPC客户端开发规范 - 基于Taro + Hono RPC的完整实现指南"

小程序RPC开发规范

概述

本文档定义了小程序端使用Taro框架结合Hono RPC客户端的标准开发规范。基于现有的mini/src/api.tsmini/src/utils/rpc-client.tsmini/src/pages/login/wechat-login.tsx中的最佳实践。

核心架构

1. RPC客户端配置

1.1 客户端初始化 (mini/src/utils/rpc-client.ts)

// 环境配置
const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000'

// 自定义fetch适配Taro.request
const taroFetch: any = async (input, init) => {
  const url = typeof input === 'string' ? input : input.url
  const method = init.method || 'GET'
  
  const requestHeaders: Record<string, string> = init.headers;

  // 自动设置content-type
  const keyOfContentType = Object.keys(requestHeaders).find(item => item.toLowerCase() === 'content-type')
  if (!keyOfContentType) {
    requestHeaders['content-type'] = 'application/json'
  }

  // 构建Taro请求选项
  const options: Taro.request.Option = {
    url,
    method: method as any,
    data: init.body,
    header: requestHeaders
  }

  // 自动添加token认证
  const token = Taro.getStorageSync('mini_token')
  if (token) {
    options.header = {
      ...options.header,
      'Authorization': `Bearer ${token}`
    }
  }

  try {
    const response = await Taro.request(options)
    
    // 处理响应数据
    const body = response.statusCode === 204
      ? null
      : responseHeaders['content-type']!.includes('application/json')
        ? JSON.stringify(response.data)
        : response.data;

    return new ResponsePolyfill(
      body,
      {
        status: response.statusCode,
        statusText: response.errMsg || 'OK',
        headers: responseHeaders
      }
    )
  } catch (error) {
    console.error('API Error:', error)
    Taro.showToast({
      title: error.message || '网络错误',
      icon: 'none'
    })
    throw error
  }
}

// 创建Hono RPC客户端工厂函数
export const rpcClient = <T extends Hono>() => {
  return hc<T>(`${API_BASE_URL}`, {
    fetch: taroFetch
  })
}

1.2 客户端API定义 (mini/src/api.ts)

import type { AuthRoutes, UserRoutes, RoleRoutes, FileRoutes } from '@/server/api'
import { rpcClient } from './utils/rpc-client'

// 创建各个模块的RPC客户端
export const authClient = rpcClient<AuthRoutes>().api.v1.auth
export const userClient = rpcClient<UserRoutes>().api.v1.users
export const roleClient = rpcClient<RoleRoutes>().api.v1.roles
export const fileClient = rpcClient<FileRoutes>().api.v1.files

使用规范

2.1 调用方式

标准GET请求

const response = await userClient.$get({
  query: {
    page: 1,
    pageSize: 10
  }
})

POST请求(带请求体)

const response = await authClient['mini-login'].$post({
  json: {
    code: loginRes.code,
    userInfo: userProfile.userInfo
  }
})

带路径参数的请求

const response = await userClient[':id'].$get({
  param: {
    id: userId
  }
})

2.2 响应处理规范

成功响应处理

if (response.status === 200) {
  const { token, user, isNewUser } = await response.json()
  
  // 保存token到本地存储
  Taro.setStorageSync('mini_token', token)
  Taro.setStorageSync('userInfo', user)
  
  // 显示成功提示
  Taro.showToast({
    title: isNewUser ? '注册成功' : '登录成功',
    icon: 'success',
    duration: 1500
  })
}

错误响应处理

try {
  const response = await authClient['mini-login'].$post({
    json: { code, userInfo }
  })
  
  if (response.status !== 200) {
    const errorData = await response.json()
    throw new Error(errorData.message || '操作失败')
  }
} catch (error: any) {
  const errorMessage = error.message || '网络错误'
  
  // 分类处理错误
  if (errorMessage.includes('用户拒绝授权')) {
    Taro.showModal({
      title: '提示',
      content: '需要授权才能使用小程序的全部功能',
      showCancel: false
    })
  } else {
    Taro.showToast({
      title: errorMessage,
      icon: 'none',
      duration: 3000
    })
  }
}

微信小程序特殊场景

3.1 微信登录流程

基于mini/src/pages/login/wechat-login.tsx的最佳实践:

const handleWechatLogin = async () => {
  try {
    Taro.showLoading({
      title: '登录中...',
      mask: true
    })

    // 1. 获取用户信息授权
    const userProfile = await Taro.getUserProfile({
      desc: '用于完善用户资料'
    })

    // 2. 获取登录code
    const loginRes = await Taro.login()
    
    if (!loginRes.code) {
      throw new Error('获取登录凭证失败')
    }

    // 3. 调用RPC接口
    const response = await authClient['mini-login'].$post({
      json: {
        code: loginRes.code,
        userInfo: userProfile.userInfo
      }
    })

    Taro.hideLoading()

    if (response.status === 200) {
      const { token, user, isNewUser } = await response.json()
      
      // 4. 保存登录态
      Taro.setStorageSync('userInfo', user)
      Taro.setStorageSync('mini_token', token)
      
      // 5. 跳转页面
      Taro.switchTab({ url: '/pages/index/index' })
    }
  } catch (error) {
    Taro.hideLoading()
    // 错误处理...
  }
}

3.2 平台检测

import { isWeapp } from '@/utils/platform'

// 检查是否为微信小程序环境
const wechatEnv = isWeapp()
if (!wechatEnv) {
  Taro.showModal({
    title: '提示',
    content: '微信登录功能仅支持在微信小程序中使用',
    showCancel: false
  })
}

开发规范

4.1 文件结构

mini/
├── src/
│   ├── api.ts              # RPC客户端定义
│   ├── utils/
│   │   └── rpc-client.ts   # RPC客户端工厂
│   └── pages/
│       └── [功能页面]/
│           └── index.tsx   # 页面逻辑

4.2 命名规范

  • 客户端命名[模块名]Client(如authClientuserClient
  • 方法命名:遵循RESTful规范(如$get$post$put$delete
  • 路径命名:使用小写字母和连字符(如mini-login

4.3 类型安全

// 使用InferResponseType提取响应类型
import type { InferResponseType } from 'hono/client'
type LoginResponse = InferResponseType<typeof authClient['mini-login']['$post'], 200>

// 使用InferRequestType提取请求类型
import type { InferRequestType } from 'hono/client'
type LoginRequest = InferRequestType<typeof authClient['mini-login']['$post']>['json']

4.4 环境配置

mini/.env中配置API地址:

TARO_APP_API_BASE_URL=https://your-api-domain.com

最佳实践

5.1 请求封装

// 创建通用请求hook
const useApiRequest = () => {
  const [loading, setLoading] = useState(false)
  
  const request = async <T>(
    apiCall: () => Promise<Response>,
    successCallback?: (data: T) => void,
    errorCallback?: (error: Error) => void
  ) => {
    setLoading(true)
    try {
      const response = await apiCall()
      const data = await response.json()
      
      if (response.status === 200) {
        successCallback?.(data)
      } else {
        throw new Error(data.message || '请求失败')
      }
    } catch (error) {
      errorCallback?.(error)
    } finally {
      setLoading(false)
    }
  }
  
  return { loading, request }
}

5.2 错误处理

const handleApiError = (error: any) => {
  const message = error.message || '网络错误'
  
  // 网络错误
  if (message.includes('Network') || message.includes('网络')) {
    Taro.showModal({
      title: '网络错误',
      content: '请检查网络连接后重试',
      showCancel: false
    })
    return
  }
  
  // 业务错误
  Taro.showToast({
    title: message,
    icon: 'none',
    duration: 3000
  })
}

5.3 加载状态管理

```typescript const [loading, setLoading] = useState(false)

const handleRequest = async () => { setLoading(true) Taro.showLoading({ title: '加载中...' })

try {

const response = await apiClient.method.$post({ json: data })
// 处理响应...

} finally {

Taro.hideLoading()
setLoading(false)

} }