更新记录

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;
}

友情链接

隐私、权限声明

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

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

插件不采集任何数据

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

许可协议

MIT协议