更新记录

1.0.5(2025-06-09) 下载此版本

  • 请求只支持await调用 不支持链式调用 request().then()这种方式
    
    const request = () ....

支持 const data = await request();

不支持 request().then(res => {...})

1.0.4(2025-06-09) 下载此版本

  • 请求只支持await调用 不支持链式调用 request().then()这种方式
    
    const request = () ....

// 支持 const data = await request();

// 不支持 request().then(res => {...})

1.0.3(2025-06-09) 下载此版本

  • 加入更多请求示例 见index.uvue
  • 提示各位开发者 UTS是一种强类型的编程语言,不要用TS的逻辑往UTS上套
如果你的type定义为
export type APIResponse = {
    code: string;
    message: string;
    data: any | 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 = {
    code: string;
    message: string;
    data?: any | null; // data使用?标识
    i18n?: string;
}
response = {code: '0000', message: '请求成功'} 不报错
response = {code: '0000', message: '请求成功', data: null} 不报错

同样的在开发页面的时候 也需要注意这种错误
如果你碰到了error message invalid json这种错误 请排查自己的Type定义
查看更多

平台兼容性

uni-app x

Chrome Safari Android iOS 鸿蒙 微信小程序
5.0 12 -

其他

多语言 暗黑模式 宽屏模式
× ×

xsd-request

  • Android/IOS已通过自定义基座测试 HarmonyNext已通过模拟器测试 真机尚不确定 欢迎小伙伴们反馈。
  • 有任何问题可直接邮件至liujiaxin@xinshidaike.com联系我
  • 如果觉得好用,请给我一个五星好评吧~
  • 同时该插件配套的一键生成代码工具正在制作中,后端接口需要符合Swagger 3.0规范(类似Alova的代码生成),敬清期待~

使用示例

import { refreshToken } from './api/api';
import { createXRequest, XBaseRequestOptions, XRequestOptions } from '@/uni_modules/xsd-request';

export type APIResponse = {
    code: string;
    message: string;
    data?: any | null;
    i18n?: string;
}

// #ifdef APP-ANDROID
@Suppress("LABEL_NAME_CLASH")
// #endif
async function _xRequest<T>(instanceConfig: RequestOptions<any>): Promise<T>{
    return createXRequest<APIResponse>(
        {
            baseUrl: 'http://192.168.6.6:6080/x-system-server',
            timeout: 1000,
            header: {
                a: '1'
            }
        } as XBaseRequestOptions
        , {
            onRequest: function(config: RequestOptions<any>) {
                //  演示方便 token存在Storage中  请自行替换为更可靠的存储方式
                config.header?.set('Authorization', `Bearer ${uni.getStorageSync('x-accessToken')}`)
                return config;
            },
            onRequestEnd: async function(response: RequestSuccess<APIResponse>) {
                if(401 == response.statusCode){
                    //  无权限/鉴权无效/鉴权过期  无感刷新Token
                    await refreshToken();
                    console.log('已刷新鉴权!');
                    //  返回null 请求重试
                    return null;
                }
                return 200 == response.statusCode;
            },
            onRequestError: function(error: IUniError, response: RequestSuccess<APIResponse> | null) {
                if(null != response){
                    uni.showToast({
                        title: `请求状态码[${response.statusCode}]`
                    })
                }else{
                    /**
                     * 如果uni.request进入了fail 则response为null
                     * 通常是网络原因 无法请求至指定接口
                     * https://doc.dcloud.net.cn/uni-app-x/api/request.html#requestfail-values
                     */
                    uni.showToast({
                        title: `请求错误码[${error.errCode}]`
                    })
                }
            },
            onRequestMonitor: function(response: APIResponse) {
                return response.code == '0000';
            },
            onRequestMonitorError: function(response: APIResponse) {
                //  请求成功 但业务失败
                uni.showToast({
                    title: `[${response.code}]${response.message}`,
                    // 国际化提示
                    // title: $t({response?.i18n})
                })
            },
            onSuccess: function(response: APIResponse): any | null {
                if(null == response.data){
                    return null;
                }
                return response.data as any | undefined
            }
        } as XRequestOptions<APIResponse>
        , instanceConfig
    ) as Promise<T>
}

export async function xRequest<T>(instanceConfig: RequestOptions<any>): Promise<T>{
    // #ifdef APP-ANDROID
    //  解决Android泛型转换失败问题
    //  此处代码暂时无法移入到onSuccess中 Android会编译错误 等待官方修复 这样也可以正常使用
    const v = await _xRequest<T>(instanceConfig) as any | null
    if(null == v){
        return null as T;
    }
    return JSON.parse<T>(JSON.stringify(v)) as T;
    // #endif

    // #ifndef APP-ANDROID
    return _xRequest<T>(instanceConfig);
    // #endif
}
/**
 * 全局请求配置 对齐官方 额外加入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;
}

/**
 * success响应 / fail响应
 */
export type XAllResponse<APIResponse> = {
    response: RequestSuccess<APIResponse> | null;
    error: IUniError | null;
}

/**
 * 请求钩子
 */
export type XRequestOptions<APIResponse> = {
    /**
     * 请求前钩子
     * 
     * 支持uni.request全部参数
     * https://doc.dcloud.net.cn/uni-app-x/api/request.html
     */
    onRequest: (config: RequestOptions<any>) => RequestOptions<any>;

    /**
     * 请求结束后钩子
     * 
     * 用于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) => any | null;
}

export interface IXError extends IUniError {};
import { xRequest } 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>({
        url: '/auth-free/accountLogin',
        method: 'POST',
        data: {
            account: 'super-admin',
            password: '123456'
        }
    } as RequestOptions<any>)
}

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 RequestOptions<any>);
    storageToken(data.accessToken, data.refreshToken);
}

export type Info = {
    id: string;
}

export const authApi = () => {
    return xRequest<Info>({
        url: '/admin/tokenGetInfo',
        method: 'GET'
    } as RequestOptions<any>)
}

export const authNull = () => {
    return xRequest<void>({
        url: '/admin/null',
        method: 'GET'
    } as RequestOptions<any>)
}

type ArrayData = {
    id: number;
    value: string;
}

export const authArray = () => {
    return xRequest<ArrayData[]>({
        url: '/admin/array',
        method: 'GET'
    } as RequestOptions<any>)
}

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
 */
export const authParams = (params: Params) => {
    return xRequest<Params>({
        url: '/admin/params',
        method: 'POST',
        data: JSON.parse<UTSJSONObject>(JSON.stringify(params))
    } as RequestOptions<any>)
}

export const authParams2 = (params: UTSJSONObject) => {
    return xRequest<Params>({
        url: '/admin/params',
        method: 'POST',
        data: params
    } as RequestOptions<any>)
}
<template>
    <view>
        <button @click="b1">调用登录接口</button>
        <button @click="b2">调用鉴权接口</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 } 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 b22 = async () => {
        await 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协议

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问