更新记录

1.4.1(2026-10-16) 下载此版本

对于加密请求返回code为失败时,不在抛出异常,直接返回 override async handleRes(req : RequestVo, res : Response) : Promise { if (resSuccessCode() != res.code) { return res // let errMsg = (res.errorMsg != null && res.errorMsg.length > 0) ? res.errorMsg : ((res.msg != null && res.msg.length > 0) ? res.msg : "") // throw CustOtherFailError("请求失败", res.code, errMsg) } let data = res.data! as string let jm = await sm4DecryptAsync(data, this.sm4Params.key, this.sm4Params.iv) let msg = jm.result let response = JSON.parse(msg)!; return response

}

1.4.0(2026-10-13) 下载此版本

更新文档

1.3.3(2026-10-12) 下载此版本

清除无效的类型导入

查看更多

平台兼容性

uni-app x(4.0)

Chrome Safari Android iOS 鸿蒙 微信小程序
√ √ √ √ √ √

sunrains-request


点击进入管理后台

在使用插件过程中遇到问题 请进群交流

服务端配套支持 1:网络请求解密,验签,返回结果加密;2:身份证ocr识别 3:人脸注册,识别,人证比对 4:活体检测 5:人脸核身 6:车牌号识别

注:活体检测、人脸核身 页面用到camera组件,camera组件官方只支持 小程序、ios、安卓端,其它平台可自己录制视频,直接调用插件中api(全平台支持)

开发文档

客户端与服务端(java)之间通信数据加密、签名,保证数据安全与防篡改 支持平台:微信小程序、H5、Android、iOS、鸿蒙 请求方式支持:GET、POST(application/json、application/x-www-form-urlencoded、multipart/form-data)

模块目录结构

sunrains-request/
├── index.uts                          # 模块入口,统一导出
├── js_sdk/
│   ├── inutils/
│   │   └── requestApi.uts             # 底层网络请求封装(uni.request / uni.uploadFile)
│   └── openutil/
│       ├── base64Util.uts             # Base64 编解码工具
│       ├── randomUtil.uts             # 随机字符串生成
│       └── request/
│           ├── Request.uts            # 请求入口(ReqApi)
│           └── reqHandle/
│               ├── ReqHandle.uts      # 请求处理抽象基类
│               ├── handle.uts         # 策略工厂(根据 reqFlag 选择 Handler)
│               └── handle/
│                   ├── VoidHandle.uts           # 无加密无签名模式
│                   ├── SignHandle.uts           # 仅签名模式
│                   ├── EncryptHandle.uts        # 仅SM4加密模式
│                   └── SignWithEncryptHandle.uts # 签名+SM4加密模式

导出 API 一览

导出函数 来源文件 说明
ReqApi request/Request.uts 高层请求入口,自动根据 reqFlag 完成签名/加密/发送/解密
RequestApi inutils/requestApi.uts 底层请求入口,直接发起 uni.request 并返回 Promise\<Response>
uploadFile inutils/requestApi.uts 文件上传,返回 Promise\<Response>
stringToBase64 openutil/base64Util.uts 字符串 → Base64 编码
base64ToString openutil/base64Util.uts Base64 → 字符串解码
random openutil/randomUtil.uts 生成指定长度的随机字符串(字母+数字),默认16位

网络请求架构(核心)

整体流程

调用方
  │
  ▼
ReqApi(frontVo: FrontRequestVo)          ← 高层入口
  │
  ├─ 1. selectHandle(reqFlag)            ← 根据模式选择 Handler
  │
  ├─ 2. handle.handleHeaderAndBody(vo)   ← 构造请求头 + 处理请求体(签名/加密)
  │
  ├─ 3. RequestApi(req)                  ← 底层发起 HTTP 请求
  │
  └─ 4. handle.handleRes(req, res)       ← 处理响应(解密/验签)
  │
  ▼
返回 Response

四种请求模式(ReqFlag)

通过 FrontRequestVo.reqFlag 指定,决定请求的安全处理策略:

reqFlag Handler 签名 请求体SM4加密 响应SM4解密 需要priKey 需要pubKey
"void" VoidHandle ✗ ✗ ✗ ✗ ✗
"sign" SignHandle ✓ SM2签名 ✗ ✗ ✓ ✗
"sm4Encrypt" EncryptHandle ✗ ✓ SM4加密 ✓ SM4解密 ✗ ✓
"signwithenSM2-Sm4Encrypt" SignWithEncryptHandle ✓ SM2签名 ✓ SM4加密 ✓ SM4解密 ✓ ✓

请求头处理(所有模式共用)

无论哪种模式,handleSignHeader 都会执行以下操作:

  1. SM2加密SM4密钥:生成随机 SM4 key + iv,用服务端公钥(pubKey)通过 SM2 加密后放入 header["key"]
  2. 条件签名(sign / signwithenSM2-Sm4Encrypt 模式):
    • 生成 timestamp(时间戳)和 nonceStr(16位随机串)
    • 将所有业务参数 + timestamp + nonceStr 按 key 字典序排列,拼接为 key=value& 格式
    • 使用客户端私钥(priKey)对拼接字符串进行 SM2 签名
    • 签名结果放入 header["sign"],同时放入 header["timestamp"] 和 header["nonceStr"]

请求体处理(按 HTTP Method + contentType)

GET 请求:

  • void / sign 模式:参数拼接到 URL query string
  • sm4Encrypt / signwithenSM2-Sm4Encrypt 模式:参数 JSON 序列化后 SM4 加密,拼接到 ?data=加密结果

POST 请求(按 contentType):

contentType 处理方式
"json" Content-Type: application/json,加密模式下 body 整体 SM4 加密为 {"data":"加密结果"}
"urlencoded" Content-Type: application/x-www-form-urlencoded,加密模式下 data=加密结果
"form-data" Content-Type: multipart/form-data

响应处理

  • 无加密模式(void / sign):直接返回 Response
  • 加密模式(sm4Encrypt / signwithenSM2-Sm4Encrypt):
    1. 先校验外层 res.code == resSuccessCode()(当前定义为 0)
    2. 取出 res.data(SM4 加密的字符串)
    3. 用本次请求生成的 SM4 key + iv 解密
    4. 解密结果为内层 Response,返回给调用方

底层请求(RequestApi)

RequestApi(req: RequestVo) → Promise<Response>
  • 基于 uni.request 封装
  • 超时时间:req.timeout ?? 6000ms
  • 成功(statusCode=200):resolve Response 对象
    • Android 平台特殊处理:通过 JSON.parse(JSON.stringify(res.data)) 转换
    • 其他平台:直接 res.data as Response
  • 失败(非200):reject CustOtherFailError(包含 code + errorMsg)
  • 网络异常:reject CustOtherFailError(包含 errCode + errMsg)

文件上传(uploadFile)

uploadFile(req: UploadFileOptions) → Promise<Response>
  • 基于 uni.uploadFile 封装
  • 超时时间:req.timeout ?? 12000ms
  • 成功时解析 res.data 为 Response
  • 失败时 reject err.errMsg

类型定义(依赖 sunrains-common-type)

FrontRequestVo(前置请求参数)

type FrontRequestVo = {
  method: 'GET' | 'POST',           // HTTP 方法
  url: string,                       // 请求地址
  data: UTSJSONObject | null,        // 请求数据
  header: UTSJSONObject | null,      // 自定义请求头(会与签名/加密头合并)
  reqFlag: ReqFlag,                  // 安全模式标识
  priKey: string,                    // SM2 私钥(签名时使用)
  pubKey: string,                    // SM2 公钥(加密SM4密钥时使用)
  contentType: "json" | "urlencoded" | "form-data" | null,  // POST内容类型
  timeout: number | null,            // 超时时间(ms)
  filePathParamName: string | null   // 上传文件时,文件路径的参数名
}

Response(统一响应)

type Response = {
  success: boolean | null,    // 是否成功
  msg: string | null,         // 提示信息
  code: number,               // 状态码(0=成功)
  errorMsg: string | null,    // 错误详情
  data: any | null            // 业务数据(加密模式下为密文字符串)
}

RequestVo(底层请求参数)

type RequestVo = {
  url: string,
  method?: 'GET' | 'POST' | null,
  header: UTSJSONObject,
  timeout?: number | null,
  data: any
}

Sm4Key(SM4密钥)

type Sm4Key = {
  key: string,
  iv: string
}

错误处理(依赖 sunrains-common-err)

所有请求错误统一通过 UniError 抛出:

错误来源 errSubject errCode message
SM2私钥为空 SM2_PRI_KEY_BLANK 预定义码 预定义信息
SM2公钥为空 SM2_PUB_KEY_BLANK 预定义码 预定义信息
SM2签名失败 SM2_SIGN_FAIL 预定义码 预定义信息
HTTP非200 "请求失败" 服务端code errorMsg / msg
网络异常 "请求失败" err.errCode err.errMsg
加密模式响应码非0 "请求失败" res.code errorMsg / msg

调用方 catch 示例:

try {
  const res = await ReqApi(frontVo)
} catch (err) {
  // 跨平台安全取值(避免 iOS 上 err.message 崩溃)
  console.log("请求异常:", `${err}`)
  // 或通过 err.message 获取错误信息(err 为 UniError 实例)
}

工具函数

Base64 编解码

import { stringToBase64, base64ToString } from "@/uni_modules/sunrains-request"

const encoded = stringToBase64("Hello")   // "SGVsbG8="
const decoded = base64ToString("SGVsbG8=") // "Hello"

随机字符串

import { random } from "@/uni_modules/sunrains-request"

const nonce = random(16)   // 生成16位随机字符串

依赖模块

模块 用途
sunrains-smutil SM2签名/加解密、SM4加解密
sunrains-common-type 类型定义(FrontRequestVo、Response、RequestVo、Sm4Key、ReqFlag)
sunrains-common-err 统一错误类(CustFailError、CustOtherFailError)

使用示例

1. 无加密请求(void)

import { ReqApi } from "@/uni_modules/sunrains-request"

const frontVo: FrontRequestVo = {
  method: "POST",
  url: "http://your-api.com/test",
  data: { name: "张三", age: 18 },
  header: null,
  reqFlag: "void",
  priKey: "",
  pubKey: "",
  contentType: "json",
  timeout: null
}
const res = await ReqApi(frontVo)

2. SM2签名请求(sign)

const frontVo: FrontRequestVo = {
  method: "POST",
  url: "http://your-api.com/test",
  data: { name: "张三", age: 18 },
  header: null,
  reqFlag: "sign",
  priKey: "客户端SM2私钥",
  pubKey: "服务端SM2公钥",
  contentType: "json",
  timeout: null
}
const res = await ReqApi(frontVo)

3. 签名 + SM4加密请求(signwithenSM2-Sm4Encrypt)

const frontVo: FrontRequestVo = {
  method: "POST",
  url: "http://your-api.com/test",
  data: { name: "张三", age: 18 },
  header: null,
  reqFlag: "signwithenSM2-Sm4Encrypt",
  priKey: "客户端SM2私钥",
  pubKey: "服务端SM2公钥",
  contentType: "json",
  timeout: null
}
const res = await ReqApi(frontVo)
// res.data 已自动解密
  1. 文件上传(为了全平台统一,只支持但文件上传(可以非文件路径参数进行 加密 签名 注:非文件参数禁止使用参数名 cufilePath))
    let fileInfo = { type: "front", htfile: frontImage.value }

    let params : FrontRequestVo = {
    method: "POST",
    url: props.ocrUrl,
    header: props.header,
    reqFlag: props.reqFlag,
    priKey:props.priKey,
    pubKey:props.pubKey,
    contentType:"form-data",
    timeout:8000,
    filePathParamName:'htfile',//文件上传必填参数,此参数也是服务端接收文件参数
    data:fileInfo
}


文件上传请求完整示例


    async function modAvatar(type:string){
    let tempFilePath = ''
    uni.chooseImage({
        count: 1,
        sizeType: ['compressed'],
        sourceType: ['album', 'camera'],
        crop: {
            width: 640,
            height: 640
        },
        success: async (res) => {
            tempFilePath = res.tempFilePaths[0]
            if(tempFilePath.length > 0){
                let fileUrl = await upLoadFile(tempFilePath,"/user/avatar","sunrains")
                if(fileUrl!=null){
                    let vo:UpdateUserInfo = {
                        type,
                        avatar:fileUrl
                    }
                    await updateUserInfo(vo,false)
                }
            }
        },
        fail: (err) => {
        }
    })
}

import { request, ResultDto } from "@/utils/request.uts"
export async function upLoadFile(tempFilePath:string,bizPath:string,bucket:string) : Promise<string|null> {
    let res : ResultDto = await request({
        url: "/app/upload/file",
        method: "POST",
        contentType: "form-data",
        showLoading: false,
        showToast: true,
        isLogin: true,
        autoJumpLogin:true,
        filePathParamName:"file",//服务端接收文件参数
        params: {
            bizPath,
            bucket,
            file:tempFilePath //file必须与filePathParamName值一致
        }
    })
    if (res.success) {
        return res.data as string
    }
    return null
}

支持与赞赏

如果你觉得本插件解决了你的问题,可以考虑支持作者:

微信赞助

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT协议