更新记录
1.0.9(2025-08-10)
下载此版本
- Android端已使用Kotlin重写,避免奇奇怪怪的编译问题(java.lang.RuntimeException: stub)
- 加入全局abort中断请求方法
- 更换局部请求参数定义,加入xId用以中断请求以及loading相关参数
- Android端抹平requestOptions.data需要转换为UTSJSONObject的差异
1.0.8(2025-07-23)
下载此版本
1.0.7(2025-07-23)
下载此版本
查看更多
平台兼容性
uni-app x(4.75)
Chrome |
Safari |
Android |
iOS |
鸿蒙 |
微信小程序 |
√ |
√ |
5.0 |
√ |
√ |
√ |
xsd-request
- Android/IOS已通过自定义基座测试 HarmonyNext已通过模拟器测试 真机尚不确定 欢迎小伙伴们反馈。
- 有任何问题可直接邮件至liujiaxin@xinshidaike.com联系我
- 如果觉得好用,请给我一个五星好评吧~
提示1
- 因官方编译器有些BUG 该版本需修改HX文件方可使用 不然Android报编译错误无法运行 后续alpha版本会更新
- BUG详细链接
- 要替换的文件我放在了示例项目中 根目录/file文件夹中 请导入示例项目后查看 如果没有私信我
- 截止至4.75版本的HBuilder X该BUG还未修复
提示2
- 提示各位开发者 UTS是一种强类型的编程语言,不要用TS的逻辑往UTS上套
如果你的type定义为
export type APIResponse<Data> = {
code: string;
message: string;
data: Data | null; data没有用?标识
i18n?: string;
}
response = {code: '0000', message: '请求成功'} UTS会报错error message invalid json TS正常data为undefind
response = {code: '0000', message: '请求成功', data: null} UTS与TS均正常
如果你的data没有用?标识,那么服务端返回包装类中就必须要有data,如果没有data属性,那么就会报error message invalid json错误
export type APIResponse<Data> = {
code: string;
message: string;
data?: Data | null; data使用?标识
i18n?: string;
}
response = {code: '0000', message: '请求成功'} 不报错
response = {code: '0000', message: '请求成功', data: null} 不报错
同样的在开发页面的时候 也需要注意这种错误
如果你碰到了error message invalid json这种错误 请排查自己的Type定义
提示3
- 如果开发者遇到java.lang.RuntimeException: stub错误,请使用4.76-2025073103-alpha以上的版本的hx进行自定义插座制作,该版本hx修的打包机修复了该问题。
使用示例
import { refreshToken } from './api';
import { createXRequest, XBaseRequestOptions, XRequestOptions, XOptions, abort } from '@/uni_modules/xsd-request';
export { XOptions, abort }
export type APIResponse<Data> = {
code: string;
message: string;
data?: Data | null;
i18n?: string;
}
export async function xRequest<T>(instanceConfig: XOptions): Promise<T>{
const base: XBaseRequestOptions = {
baseUrl: 'http://192.168.6.6:6080/x-hl-server',
timeout: 30000,
header: {
a: '1'
},
}
const x: XRequestOptions<APIResponse<T>, T> = {
// config只能写箭头函数 写function在Android下报编译错误
onRequest: config => {
config.header?.set('Authorization', `Bearer ${uni.getStorageSync('x-accessToken')}`);
return config;
},
onRequestEnd: async function(response: RequestSuccess<APIResponse<T>>) {
if(401 == response.statusCode){
await refreshToken();
return null;
}
return 200 == response.statusCode;
},
onRequestError: function(error: IUniError, response: RequestSuccess<APIResponse<T>> | null) {
if (null != response) {
uni.showToast({
title: `请求状态码[${response.statusCode}]`
});
} else {
// 在Android和iOS下 task.abort()后会进入这里 errCode = 602001
uni.showToast({
title: `请求错误码[${error.errCode}]`
});
}
},
onRequestMonitor: function(response: APIResponse<T>) {
return response.code == '0000';
},
onRequestMonitorError: function(response: APIResponse<T>) {
// 请求成功 但业务失败
uni.showToast({
title: `[${response.code}]${response.message}`,
});
},
onSuccess: function(response: APIResponse<T>) {
return response.data as T | null;
}
}
return createXRequest<APIResponse<T>, T>(base, x, instanceConfig);
}
/**
* 全局请求配置 对齐官方 额外加入baseUrl
* https://doc.dcloud.net.cn/uni-app-x/api/request.html
*/
export type XBaseRequestOptions = {
baseUrl: string;
header ?: UTSJSONObject;
timeout ?: number;
withCredentials ?: boolean;
firstIpv4 ?: boolean;
}
/**
* 局部请求配置 对齐官方
* https://doc.dcloud.net.cn/uni-app-x/api/request.html
*/
export type XOptions = {
// 官方参数
url: string,
data?: any | null,
header?: UTSJSONObject | null,
method: RequestMethod,
timeout?: number | null,
sslVerify?: boolean | null,
withCredentials?: boolean | null,
firstIpv4?: boolean | null,
// 唯一请求ID
xId?: string,
// 请求前是否uni.showLoading 默认true
loading?: boolean,
// loading 的 title 默认""
loadingTitle?: string,
// 是否显示透明蒙层,防止触摸穿透 默认true
loadingMask?: boolean
}
/**
* success响应 / fail响应
*/
export type XAllResponse<Data> = {
response: RequestSuccess<Data> | null;
error: IUniError | null;
}
/**
* 请求钩子
*/
export type XRequestOptions<APIResponse, Data> = {
/**
* 请求前钩子
*
* 支持uni.request全部参数
* https://doc.dcloud.net.cn/uni-app-x/api/request.html
*/
onRequest: (config: XOptions) => XOptions;
/**
* 请求结束后钩子
*
* 用于http是否成功
*/
onRequestEnd: (response: RequestSuccess<APIResponse>) => Promise<boolean | null>;
/**
* 请求失败钩子
*
* 由onRequestEnd返回false调用
*/
onRequestError: (error: IUniError, response: RequestSuccess<APIResponse> | null) => void;
/**
* 业务是否成功钩子
*
* 由onRequestEnd返回true调用
*/
onRequestMonitor: (response: APIResponse) => boolean;
/**
* 业务错误钩子
*
* 由onRequestMonitor返回false调用
*
*/
onRequestMonitorError: (response: APIResponse) => void;
/**
* 业务成功钩子
*
* 由onRequestMonitor返回ture调用
* 用于提出出包装类中的数据
*/
onSuccess: (response: APIResponse) => Data | null;
}
import { xRequest, XOptions, abort } from "../request";
export type XToken = {
accessToken : string;
refreshToken : string;
}
export const storageToken = (accessToken: string, refreshToken: string) => {
uni.setStorageSync('x-accessToken', accessToken);
uni.setStorageSync('x-refreshToken', refreshToken);
}
export const doLogin = async () => {
return xRequest<XToken>({
xId: new Date().getTime() + "",
url: '/auth-free/accountLogin',
method: 'POST',
data: {
account: 'super-admin',
password: '123456'
}
} as XOptions);
}
export const refreshToken = async () => {
const _refreshToken = uni.getStorageSync('x-refreshToken');
const data = await xRequest<XToken>({
url: '/auth-free/refreshToken',
method: 'POST',
data: {
refreshToken: _refreshToken
}
} as XOptions);
storageToken(data.accessToken, data.refreshToken);
}
export type Info = {
id: string;
}
export const authApi = () => {
return xRequest<Info>({
xId: "666666666666666",
loading: true,
loadingTitle: '加载中111',
loadingMask: true,
url: '/admin/tokenGetInfo',
method: 'GET'
} as XOptions)
}
export const abortAny = () => {
// abort方法可在全局任意处调用 通过请求唯一ID随时中断请求
abort("666666666666666");
}
export const authNull = () => {
xRequest<void>({
url: '/admin/null',
method: 'GET'
} as XOptions)
}
type ArrayData = {
id: number;
value: string;
}
export const authArray = () => {
return xRequest<ArrayData[]>({
url: '/admin/array',
method: 'GET'
} as XOptions)
}
export type Params = {
params1: string;
params2: string;
}
/**
* https://doc.dcloud.net.cn/uni-app-x/api/request.html
* app-android端 参数只能为UTSJSONObject或者string
* app-android平台从 4.51版本开始支持ArrayBuffer, app-ios平台从 4.61版本开始支持ArrayBuffer
*
* 1.0.9版本 Android端已抹平该差异 直接原类型入参即可
*/
export const authParams = (params: Params) => {
return xRequest<Params>({
url: '/admin/params',
method: 'POST',
// data: JSON.parseObject<UTSJSONObject>(JSON.stringify(params))
data: params
} as XOptions)
}
export const authParams2 = (params: UTSJSONObject) => {
return xRequest<Params>({
url: '/admin/params',
method: 'POST',
data: params
} as XOptions)
}
<template>
<view>
<button @click="b1">调用登录接口</button>
<button @click="b2">调用鉴权接口</button>
<button @click="b2s">中断鉴权接口</button>
<button @click="b22">调用返回null接口</button>
<button @click="b23">调用返回Array接口</button>
<input style="height: 50px; border: 1px solid #ddd;" v-model="params.params1" placeholder="参数1"/>
<input style="height: 50px; border: 1px solid #ddd;" v-model="params.params2" placeholder="参数2"/>
<button @click="b24">接口传参示例1</button>
<button @click="b25">接口传参示例2</button>
<button @click="b3">删除accessToken(模拟未鉴权)</button>
<button @click="b4">修改assessToken(模拟鉴权过期或无效)</button>
<button @click="b5">清空全部token</button>
<button @click="b6">UTS强类型示意</button>
</view>
</template>
<script lang="uts" setup>
import { Params, authApi, authArray, authNull, authParams, authParams2, doLogin, storageToken, abortAny } from '@/service';
const params = reactive<Params>({
params1: '',
params2: ''
})
const b1 = async () => {
const data = await doLogin();
// 如果请求失败 代码会被throw error阻断 下方代码不会执行
console.log('登录接口数据=', data);
storageToken(data.accessToken, data.refreshToken);
/*
14:45:11.788 登录接口数据= [XToken] {accessToken: "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJETC1YU0QiLCJpYXQiOjE3NDk0NTE1MTEsImV4cCI6MTc0OTQ1ODcxMSwianRpIjoiMSJ9.4VMEi-c7V6PEKV_XO1R55erKejgjvAspSanAZm_yvPI", refreshToken: "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJETC1YU0QiLCJpYXQiOjE3NDk0NTE1MTEsImV4cCI6MTc0OTUzNzkxMSwianRpIjoiMSJ9.whTKvqTlMvD0N8Dzo7YaedeYGj_qs30OO_psCnpaYyE"} at pages/index/index.uvue:28
*/
}
const b2 = async () => {
const data = await authApi();
console.log('鉴权接口数据=', data);
uni.showToast({
title: data.id
})
/*
14:45:29.186 鉴权接口数据= [Info] {id: "1"} at pages/index/index.uvue:34
*/
}
const b2s = () => {
abortAny();
}
const b22 = () => {
authNull();
console.log('已调用返回null的接口')
uni.showToast({
title: '已调用'
})
/*
14:45:45.447 已调用返回null的接口 at pages/index/index.uvue:42
*/
}
const b23 = async () => {
const data = await authArray();
console.log('返回array接口=', data)
console.log('array[0]=', data[0])
console.log('array[1]=', data[1])
uni.showToast({
title: JSON.stringify(data)
})
/*
14:45:58.056 返回array接口= [Array] [ {id: 2, value: "A2"}, {id: 2, value: "A2"} ] at pages/index/index.uvue:50
14:45:58.056 array[0]= [ArrayData] {id: 2, value: "A2"} at pages/index/index.uvue:51
14:45:58.056 array[1]= [ArrayData] {id: 2, value: "A2"} at pages/index/index.uvue:5
*/
}
const b24 = async () => {
/**
* authParams方法会将params转换为UTSJSONObject
*/
const data = await authParams(params);
console.log('传参示例1=', data)
uni.showToast({
title: JSON.stringify(data)
})
/*
14:46:28.783 传参示例1= [Params] {params1: "参数1", params2: "参数22222222"} at pages/index/index.uvue:63
*/
}
const b25 = async () => {
const p = {
params1: params.params1,
params2: params.params2
} as UTSJSONObject
const data = await authParams2(p);
console.log('传参示例2=', data)
uni.showToast({
title: JSON.stringify(data)
})
/*
14:46:39.646 传参示例2= [Params] {params1: "参数1", params2: "参数22222222"} at pages/index/index.uvue:76
*/
}
const b3 = () => {
uni.removeStorageSync('x-accessToken');
console.log('已删除accessToken')
uni.showToast({
title: '已删除'
})
}
const b4 = () => {
uni.setStorageSync('x-accessToken', '1111');
console.log('已修改accessToken')
uni.showToast({
title: '已修改'
})
}
const b5 = () => {
uni.clearStorageSync();
console.log('已清空Storage')
uni.showToast({
title: '已清空'
})
}
type A = {
code: string;
data: string;
}
type B = {
code: string;
data?: string;
}
const b6 =() => {
// UTS与TS均不报错 UTS的data为null TS的data为undefined
console.log(JSON.parse<B>(JSON.stringify({code: '0000'})));
// UTS报错 TS不报错 data为undefined
console.log(JSON.parse<A>(JSON.stringify({code: '0000'})));
}
</script>
<style>
</style>
服务端示例代码
@GetMapping("/null")
public void nullGet() {
}
@GetMapping("/array")
public JSONArray authArray() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", 1);
jsonObject.put("value", "A1");
JSONArray jsonArray = new JSONArray();
jsonArray.add(jsonObject);
jsonObject.put("id", 2);
jsonObject.put("value", "A2");
jsonArray.add(jsonObject);
return jsonArray;
}
@PostMapping("/params")
public JSONObject params(@RequestBody JSONObject jsonObject) {
return jsonObject;
}
友情链接