refactor: 确立独立子工程架构 (backend, admin, mpuniapp) 并同步更新 AGENTS.md 规范

这个提交包含在:
wikiboss
2026-09-20 17:05:22 +08:00
父节点 de3cf8b870
当前提交 9fa1d1fbd0
共有 1679 个文件被更改,包括 204798 次插入 和 16817 次删除

查看文件

@@ -0,0 +1,261 @@
const platformInfo = process.env.uniTestPlatformInfo.toLocaleLowerCase()
const isAndroid = platformInfo.startsWith('android')
const isIOS = platformInfo.startsWith('ios')
const isHarmony = platformInfo.startsWith('harmony')
const isWeb = platformInfo.startsWith('web')
const isMP = platformInfo.startsWith('mp')
const isAppWebView = process.env.UNI_AUTOMATOR_APP_WEBVIEW == 'true'
describe('ExtApi-Request', () => {
if (isMP) {
it('skip', async () => {
expect(1).toBe(1);
});
return;
}
const PAGE_PATH = '/pages/API/request/request'
const methodMap = {
"GET": "/api/http/method/get",
"POST": "/api/http/method/post",
"PUT": "/api/http/method/put",
"DELETE": "/api/http/method/delete",
"PATCH": "/api/http/method/patch",
"OPTIONS": "/api/http/method/options",
"HEAD": "/api/http/method/head"
}
let page;
let res;
// 测试辅助函数
async function setPageData(newData) {
return await page.setData({ data: newData });
}
async function request(page, method, header, data, url) {
if (url == null) {
url = methodMap[method]
}
await setPageData({
url: url,
method: method,
data: data,
header: header
})
res = await page.callMethod('jest_request')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
}
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor('view');
});
beforeEach(async () => {
await setPageData({
jest_result: false,
data: null,
header: null
})
});
it('Check GET', async () => {
await request(page, 'GET');
});
it('Check POST ContentType Json', async () => {
await request(page, 'POST', {
"Content-Type": "application/json"
}, {
"hello": "world"
}, "/api/http/contentType/json");
});
it('Check POST ContentType Form', async () => {
await request(page, 'POST', {
"Content-Type": "application/x-www-form-urlencoded"
}, "hello=world", "/api/http/contentType/xWwwFormUrlencoded");
});
it('Check PUT', async () => {
await request(page, 'PUT');
});
it('Check DELETE', async () => {
await request(page, 'DELETE');
});
// 鸿蒙平台暂不支持PATCH方法
if (!isHarmony) {
it('Check PATCH', async () => {
await request(page, 'PATCH');
});
}
if (process.env.uniTestPlatformInfo.indexOf('web') === -1) {
it('Check OPTIONS', async () => {
await request(page, 'OPTIONS');
});
}
it('Check HEAD', async () => {
await request(page, 'HEAD');
});
if (!isMP) {
it('Check Request Header Lowercase', async () => {
res = await page.callMethod('jest_check_header_lowercase')
await page.waitFor(3000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
}
it('Request with timeout null', async () => {
res = await page.callMethod('jest_timeout_null')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
});
it('Get Array', async () => {
res = await page.callMethod('jest_get_array')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
let shouldTestCookie = false
if (isAndroid && !isAppWebView) {
let version = process.env.uniTestPlatformInfo
version = parseInt(version.split(" ")[1])
shouldTestCookie = version > 9
}
if (isIOS && !isAppWebView) {
shouldTestCookie = true
}
if (shouldTestCookie) {
it('Check Set Cookie', async () => {
res = await page.callMethod('jest_set_cookie')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
});
it('Check Delete Cookie', async () => {
res = await page.callMethod('jest_delete_cookie')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
});
it('Check Set Cookie Expires', async () => {
await page.callMethod('jest_set_cookie_expires')
await page.waitFor(2000);
res = await page.data('data.jest_result_data');
console.log("request expires cookie data :", res);
res = await page.data('data.jest_result');
expect(res).toBe(true)
await setPageData({
jest_result: false,
jest_result_data: "",
data: null,
header: null
})
await page.waitFor(5000);
await page.callMethod('jest_cookie_request', false)
await page.waitFor(2000);
res = await page.data('data.jest_result_data');
console.log("verify request data :", res);
res = await page.data('data.jest_result');
expect(res).toBe(true)
});
}
it('Check Get With Data', async () => {
res = await page.callMethod('jest_get_with_data')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
it('Check Get With Generics', async () => {
res = await page.callMethod('jest_get_with_generics')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
// 15以下的模拟器所对应的xcode不能编译自定义插件,大于15是因为某台设备,会用xcode14.1跑15.5的设备
let version = process.env.uniTestPlatformInfo
let split = version.split(" ")
version = parseInt(split[split.length - 1])
if (isIOS && version > 15 || isAndroid || isHarmony) {
it('Check Post In UTS Module', async () => {
res = await page.callMethod('jest_uts_module_invoked')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
}
it('Check Respone Json String', async () => {
res = await page.callMethod('jest_respone_json_string')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
if (isIOS) {
it('Check iOS issue21823 Crash Bug', async () => {
res = await page.callMethod('jest_test_issue21823_crash')
await page.waitFor(2000);
res = await page.data('data.jest_complete');
expect(res).toBe(true)
})
}
if (process.env.uniTestPlatformInfo.toLocaleLowerCase().startsWith('android')) {
it('Check Respone With String Generics', async () => {
res = await page.callMethod('jest_respone_with_string_generics')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
}
if (process.env.uniTestPlatformInfo.toLocaleLowerCase().startsWith('android')) {
it('Check Respone string generics when status is 404', async () => {
res = await page.callMethod('jest_respone_with_404_and_string_generics')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
}
if (isAndroid || isIOS) {
it('send arraybuffer', async () => {
res = await page.callMethod('sendArrayBuffer', true)
await page.waitFor(5000);
res = await page.data('data.res');
expect(res).toEqual('请求结果 : 123,34,104,101,108,108,111,34,58,34,119,111,114,108,100,34,125')
})
}
if(isWeb) {
it('issue 19687', async () => {
await page.callMethod('jest_issue_19687')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
}
it('gzip-test', async () => {
res = await page.callMethod('sendGzipRequest')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
it('checkQuery', async () => {
res = await page.callMethod('checkQuery')
await page.waitFor(2000);
res = await page.data('data.jest_result');
expect(res).toBe(true)
})
});

查看文件

@@ -0,0 +1,859 @@
<template>
<view style="flex: 1;">
<page-intro content="本页演示 uni.request:设置请求方式(GET/POST/PUT 等)、请求地址与参数,发起请求后在上方查看返回内容;可切换错误码接口、不同 header、超时等,并展示请求任务与拦截器用法。"></page-intro>
<view class="uni-padding-wrap uni-common-mt">
<view class="uni-common-mt" style="border-width: 2px;border-style: solid; border-radius: 4px;">
<textarea :value="data.res" class="uni-textarea" style="width: 100%"></textarea>
</view>
<view>
<text>地址 : {{ data.host + data.url}}</text>
<text>请求方式 : {{data.method}}</text>
</view>
<view class="uni-btn-v uni-common-mt">
<button type="primary" @click="sendRequest">发起请求</button>
</view>
</view>
<scroll-view style="flex: 1;" :show-scrollbar="true">
<view style="padding: 20px;">
<text>设置请求方式</text>
<view class="uni-common-pb"></view>
<view style="flex-direction: row;flex-wrap: wrap;">
<button style="padding: 5px; margin-right: 10px;" type="primary" size="mini"
@click="changeMethod('GET')">GET</button>
<button style="padding: 5px; margin-right: 10px; " type="primary" size="mini"
@click="changeMethod('POST')">POST</button>
<button style="padding: 5px; margin-right: 10px; " type="primary" size="mini"
@click="changeMethod('PUT')">PUT</button>
<button style="padding: 5px; margin-right: 10px;" type="primary" size="mini"
@click="changeMethod('DELETE')">DELETE</button>
<button style="padding: 5px; margin-right: 10px; " type="primary" size="mini"
@click="changeMethod('PATCH')">PATCH</button>
<button style="padding: 5px;margin-right: 10px;" type="primary" size="mini"
@click="changeMethod('OPTIONS')">OPTIONS</button>
<button style="padding: 5px;" type="primary" size="mini" @click="changeMethod('HEAD')">HEAD</button>
</view>
</view>
<view style="padding: 20px;">
<text>请求返回错误码的接口(默认为GET)</text>
<view class="uni-common-pb"></view>
<view style="flex-direction: row;flex-wrap: wrap;">
<button style="padding: 5px;" type="primary" size="mini" v-for="(item, index) in data.errorCodeUrls" :key="index"
@click="changeUrl(item)">{{item}}</button>
</view>
</view>
<view style="padding: 20px;">
<text>请求不同header的接口(默认为GET)</text>
<view class="uni-common-pb"></view>
<view style="flex-direction: row;flex-wrap: wrap;">
<button style="padding: 5px;" type="primary" size="mini" v-for="(item, index) in data.headerUrls" :key="index"
@click="changeUrl(item)">{{item}}</button>
</view>
</view>
<!-- #ifdef APP-ANDROID || APP-IOS || APP-HARMONY || WEB -->
<view style="padding: 20px;">
<text>检测请求 header 键是否为小写</text>
<view class="uni-common-pb"></view>
<button type="primary" size="mini" @click="checkHeaderLowerCase">检测 header 键小写</button>
</view>
<!-- #endif -->
<view style="padding: 20px;">
<text>请求不同content-type的接口(默认为GET)</text>
<view class="uni-common-pb"></view>
<view style="flex-direction: row;flex-wrap: wrap;">
<button style="padding: 5px;" type="primary" size="mini" v-for="(item, index) in data.contentTypeUrls" :key="index"
@click="changeUrl(item)">{{item}}</button>
</view>
</view>
<view style="padding: 20px;">
<text>POST请求(有body)</text>
<view class="uni-common-pb"></view>
<view style="flex-direction: row;flex-wrap: wrap;">
<button style="padding: 5px;" type="primary" size="mini" v-for="(item, index) in data.postUrls" :key="index"
@click="changeUrlFromPost(item)">{{item}}</button>
</view>
</view>
<!-- #ifdef APP || MP-WEIXIN || WEB -->
<view style="padding: 20px;">
<text>ArrayBuffer 测试</text>
<view class="uni-common-pb"></view>
<view style="flex-direction: row;flex-wrap: wrap;">
<button style="padding: 5px;" type="primary" size="mini"
@click="sendArrayBuffer(false)">请求body为ArrayBuffer,response也为ArrayBuffer</button>
<button style="padding: 5px;" type="primary" size="mini"
@click="sendArrayBuffer(true)">请求response为ArrayBuffer</button>
</view>
</view>
<!-- #endif -->
<view style="padding: 20px;">
<text>RequestTask 测试</text>
<view class="uni-common-pb"></view>
<button type="primary" @click="sendChunkRequest">流式请求</button>
</view>
<view style="padding: 20px;">
<text>enableQuic 测试</text>
<view class="uni-common-pb"></view>
<button type="primary" @click="sendH3Request">http3请求</button>
</view>
<view style="padding: 20px;">
<text>Accept-Encoding:'gzip'测试</text>
<view class="uni-common-pb"></view>
<button type="primary" @click="sendGzipRequest">Accept-Encoding:'gzip'请求</button>
</view>
<view style="padding: 20px;">
<text>检测query</text>
<view class="uni-common-pb"></view>
<button type="primary" @click="checkQuery">检测query</button>
</view>
<view class="uni-common-pb"></view>
<view class="uni-common-pb"></view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
// #ifdef APP-ANDROID || APP-IOS || APP-HARMONY
import {
testInovkeRequest,
CommonOptions
} from '@/uni_modules/test-invoke-network-api'
// #endif
class GETDataType {
data : UTSJSONObject | null = null
}
const duration = 2000
const methodMap = {
"GET": "/api/http/method/get",
"POST": "/api/http/method/post",
"PUT": "/api/http/method/put",
"DELETE": "/api/http/method/delete",
"PATCH": "/api/http/method/patch",
"OPTIONS": "/api/http/method/options",
"HEAD": "/api/http/method/head"
}
const h3url = "https://request-h3.dcloud.net.cn/api/http/protocol";
type DataType = {
title: string;
res: string;
task: RequestTask | null;
host: string;
url: string;
method: RequestMethod | null;
data: any | null;
header: UTSJSONObject | null;
errorCodeUrls: string[];
headerUrls: string[];
contentTypeUrls: string[];
postUrls: string[];
jest_requestTask_result: boolean;
jest_result: boolean;
jest_result_data: string;
jest_complete: boolean;
}
const data = reactive({
title: 'request',
res: '',
task: null,
host: "https://request.dcloud.net.cn",
url: "/api/http/method/get",
method: "GET",
data: null,
header: null,
errorCodeUrls: [
"/api/http/statusCode/200",
"/api/http/statusCode/204",
"/api/http/statusCode/301",
"/api/http/statusCode/302",
"/api/http/statusCode/307",
"/api/http/statusCode/400",
"/api/http/statusCode/401",
"/api/http/statusCode/403",
"/api/http/statusCode/404",
"/api/http/statusCode/405",
"/api/http/statusCode/500",
"/api/http/statusCode/502",
"/api/http/statusCode/503",
"/api/http/statusCode/504",
],
headerUrls: [
"/api/http/header/ua",
"/api/http/header/referer",
"/api/http/header/requestCookie",
"/api/http/header/setCookie",
"/api/http/header/deleteCookie"
],
contentTypeUrls: [
"/api/http/contentType/text/plain",
"/api/http/contentType/text/html",
"/api/http/contentType/text/xml",
"/api/http/contentType/image/gif",
"/api/http/contentType/image/jpeg",
"/api/http/contentType/image/png",
"/api/http/contentType/application/json",
"/api/http/contentType/application/octetStream",
],
postUrls: [
"/api/http/contentType/json",
"/api/http/contentType/xWwwFormUrlencoded",
],
//自动化测试例专用
jest_requestTask_result: false,
jest_result: false,
jest_complete: false,
jest_result_data: ""
} as DataType)
onUnload(() => {
uni.hideLoading();
data.task?.abort();
})
const sendChunkRequest = () => {
uni.navigateTo({
url: "/pages/API/request/requestTask"
})
}
const sendH3Request = () => {
uni.showLoading({
title: "请求中..."
})
uni.request<string>({
url:h3url,
// #ifdef APP-ANDROID || APP-IOS || APP-HARMONY
enableQuic:true,
// #endif
dataType: 'text',
success:(res) => {
data.res = JSON.stringify(res)
uni.hideLoading();
uni.showToast({
title: '请求成功',
icon: 'success',
duration: duration
})
console.log("res: ", res);
},
fail:(e) => {
data.res = e.errMsg
uni.hideLoading();
uni.showToast({
title: '请求失败',
icon: 'error',
duration: duration
})
console.log("error: ", e);
}
})
}
const sendGzipRequest = () => {
uni.request({
url: data.host + methodMap["POST"],
header: {
'Accept-Encoding': 'gzip'
},
method:"POST",
data:{
"hello": "world"
},
success:(res) => {
console.log("res: ", res);
data.jest_result = true;
},
fail: (e) => {
console.log("error: ", e);
data.jest_result = false;
}
})
}
const checkQuery = () => {
uni.request<string>({
url: "https://request.dcloud.net.cn/api/http/echo/text?Signature=whpMFJg%2B%2Fc0tqdNXxo4330muVdc%3D",
// #ifndef APP-ANDROID && UNI-APP-X && !VUE3-VAPOR
dataType: 'text',
// #endif
success:(res) => {
data.jest_result = res.data == "Signature=whpMFJg%2B%2Fc0tqdNXxo4330muVdc%3D\n";
console.log("res: ", res);
},
fail: (e) => {
console.log("error: ", e);
data.jest_result = false;
}
})
}
const changeMethod = (e : RequestMethod) => {
data.method = e;
data.url = methodMap[e] as string;
data.data = null;
data.header = null;
}
const changeUrl = (e : string) => {
data.method = "GET";
data.url = e;
data.data = null;
data.header = null;
}
const changeUrlFromPost = (e : string) => {
data.method = "POST";
data.url = e;
switch (e) {
case "/api/http/contentType/json":
data.header = {
"Content-Type": "application/json"
};
data.data = {
"hello": "world"
};
break;
case "/api/http/contentType/xWwwFormUrlencoded":
data.header = {
"Content-Type": "application/x-www-form-urlencoded"
};
data.data = "hello=world";
break;
}
}
function getResponseHeaderObject(header : any): UTSJSONObject {
return JSON.parse(JSON.stringify(header)) as UTSJSONObject
}
const checkHeaderLowerCase = () => {
uni.showLoading({
title: "请求中..."
})
uni.request<UTSJSONObject>({
url: data.host + "/api/http/contentType/json",
method: "POST",
header: {
"Content-Type": "application/json"
},
data: {
"hello": "world"
},
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: (res : RequestSuccess<UTSJSONObject>) => {
const responseHeader = getResponseHeaderObject(res.header)
const lowerCaseValue = responseHeader.getString("content-type") ?? ""
const upperCaseValue = responseHeader.getString("Content-Type")
data.jest_result = lowerCaseValue == "application/json" && upperCaseValue == null
data.res = '请求结果 : ' + JSON.stringify(res.data)
uni.showToast({
title: data.jest_result ? '检测成功' : '检测失败',
icon: data.jest_result ? 'success' : 'error',
duration: duration
})
},
fail: (err : RequestFail) => {
data.jest_result = false
data.res = err.errMsg
uni.showToast({
title: '请求失败',
icon: 'error',
duration: duration
})
},
complete: () => {
uni.hideLoading()
}
})
}
// #ifdef APP | MP-WEIXIN | WEB
const sendArrayBuffer = (onlyResponse : boolean) => {
data.method = "POST"
data.url = "/api/http/contentType/arrayBuffer/post"
if (onlyResponse) {
data.header = {
"Content-Type": "application/json"
};
data.data = {
"hello": "world"
};
uni.showLoading({
title: "请求中..."
})
data.task = uni.request<ArrayBuffer>({
url: data.host + data.url,
method: data.method,
data: data.data,
header: data.header,
timeout: 60000,
sslVerify: false,
withCredentials: false,
responseType: 'arraybuffer',
firstIpv4: false,
success: (res) => {
if (res.data != null) {
let uint8 = new Uint8Array(res.data as ArrayBuffer)
console.log('request success', uint8.toString())
data.res = '请求结果 : ' + uint8.toString()
console.log('request success header is :', JSON.stringify(res.header))
uni.showToast({
title: '请求成功',
icon: 'success',
mask: true,
duration: duration
});
} else {
uni.showModal({
content: 'error',
showCancel: false
});
}
},
fail: (err) => {
console.log('request fail', err);
uni.showModal({
content: err.errMsg,
showCancel: false
});
},
complete: () => {
uni.hideLoading()
data.task = null
},
});
} else {
uni.chooseImage({
count: 1,
albumMode: "system",
sizeType: ["original"],
sourceType: ["album"],
success: (e) => {
console.log(e.tempFilePaths[0]);
uni.getFileSystemManager().readFile({
filePath: e.tempFilePaths[0],
success: (res) => {
data.data = res.data as ArrayBuffer
data.header = {
"Content-Type": "application/octet-stream"
};
uni.showLoading({
title: "请求中..."
})
data.task = uni.request<ArrayBuffer>({
url: data.host + data.url,
method: data.method,
data: data.data,
header: data.header,
timeout: 60000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
responseType: 'arraybuffer',
success: (res) => {
console.log()
if (res.data != null) {
uni.showToast({
title: '请求成功',
icon: 'success',
mask: true,
duration: duration
});
data.res = '请求结果 : byteLength=' + (res.data as ArrayBuffer).byteLength
console.log('request success header is :', JSON.stringify(res.header))
} else {
uni.showModal({
content: 'error',
showCancel: false
});
}
},
fail: (err) => {
console.log('request fail', err);
uni.showModal({
content: err.errMsg,
showCancel: false
});
},
complete: () => {
uni.hideLoading()
data.task = null
},
});
}
})
}
});
}
}
// #endif
const sendRequest = () => {
uni.showLoading({
title: "请求中..."
})
data.task = uni.request({
url: data.host + data.url,
// dataType: "json",
// responseType: "json",
method: data.method,
data: data.data,
header: data.header,
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: (res) => {
console.log('request success', JSON.stringify(res.data))
console.log('request success header is :', JSON.stringify(res.header))
uni.showToast({
title: '请求成功',
icon: 'success',
mask: true,
duration: duration
});
data.res = '请求结果 : ' + JSON.stringify(res);
},
fail: (err) => {
console.log('request fail', err);
uni.showModal({
content: err.errMsg,
showCancel: false
});
},
complete: () => {
uni.hideLoading()
data.task = null
},
});
}
//自动化测试例专用
const jest_request = () => {
uni.request({
url: data.host + data.url,
// dataType: "json",
// responseType: "json",
method: data.method,
data: data.data,
header: data.header,
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: () => {
data.jest_result = true;
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_cookie_request = (needCookie : boolean) => {
uni.request({
url: data.host + "/api/http/header/requestCookie",
method: "GET",
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: (res) => {
const requestCookie = (res.data as UTSJSONObject).getJSON("data")?.getAny("requestCookie")
data.jest_result_data = JSON.stringify(requestCookie)
if (requestCookie instanceof Array) {
data.jest_result = needCookie ? requestCookie.length > 0 : requestCookie.length == 0
} else {
data.jest_result = needCookie ? (requestCookie as UTSJSONObject).toMap().size > 0 : (requestCookie as UTSJSONObject).toMap().size == 0
}
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_set_cookie = () => {
uni.request({
url: data.host + "/api/http/header/setCookie",
method: "GET",
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: () => {
jest_cookie_request(true)
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_set_cookie_expires = () => {
uni.request({
url: data.host + "/api/http/header/setCookie?expires=5",
method: "GET",
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: () => {
jest_cookie_request(true)
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_delete_cookie = () => {
uni.request({
url: data.host + "/api/http/header/deleteCookie",
method: "GET",
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: () => {
jest_cookie_request(false)
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_timeout_null = () => {
uni.request({
url: data.host + (methodMap['GET'] as string),
method: "GET",
timeout: null,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: () => {
data.jest_result = true;
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_get_with_data = () => {
uni.request({
url: "https://unidemo.dcloud.net.cn/api/banner/36kr",
method: "GET",
data: {
column: 'id,post_id,title,author_name,cover,published_at' //需要的字段名
},
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: () => {
data.jest_result = true;
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_get_with_generics = () => {
uni.request<GETDataType>({
url: data.host + (methodMap['GET'] as string),
method: "GET",
timeout: null,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: (res : RequestSuccess<GETDataType>) => {
data.jest_result = true;
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_get_array = () => {
uni.request<UTSJSONObject[]>({
url: 'https://unidemo.dcloud.net.cn/api/news?column=title,author_name,cover,published_at',
method: "GET",
success: (res : RequestSuccess<UTSJSONObject[]>) => {
if (res.statusCode == 200 && Array.isArray(res.data)) {
data.jest_result = true
} else {
data.jest_result = false
}
},
fail: () => {
data.jest_result = false
}
});
}
const jest_uts_module_invoked = () => {
// #ifdef APP-ANDROID || APP-IOS || APP-HARMONY
data.jest_result = false
const options : CommonOptions = {
success: (res : any) => {
data.jest_result = true
},
fail: (err : any) => {
data.jest_result = false
}
}
testInovkeRequest(options)
// #endif
}
const jest_respone_json_string = () => {
uni.request({
url: "https://request.dcloud.net.cn/api/http/contentType/text/json",
success: (res : RequestSuccess<any>) => {
data.jest_result = typeof res.data == "object"
},
fail: (e : RequestFail) => {
data.jest_result = false
}
})
}
const jest_respone_with_string_generics = () => {
uni.request<string>({
url: data.host + (methodMap['GET'] as string),
method: "GET",
timeout: null,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: (res : RequestSuccess<string>) => {
data.jest_result = true;
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_respone_with_404_and_string_generics = () => {
uni.request<string>({
url: data.host + "/api/http/statusCode/404",
method: "GET",
timeout: null,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: (res : RequestSuccess<string>) => {
data.jest_result = true;
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_issue_19687 = () => {
uni.request({
url: data.host + "/api/http/statusCode/404",
method: "GET",
success: (res) => {
data.jest_result = typeof res.data === 'string';
},
fail: () => {
data.jest_result = false;
},
});
}
const jest_test_issue21823_crash = () => {
uni.request<string>({
url: "http://www.kld20s.cn:88/Appfile/App/Tcrysta/kld/TcnAppUpgrade.json",
method: "GET",
timeout: null,
complete: () => {
data.jest_complete = true;
}
});
}
const jest_check_header_lowercase = () => {
uni.request<UTSJSONObject>({
url: data.host + "/api/http/contentType/json",
method: "POST",
header: {
"Content-Type": "application/json"
},
data: {
"hello": "world"
},
timeout: 6000,
sslVerify: false,
withCredentials: false,
firstIpv4: false,
success: (res : RequestSuccess<UTSJSONObject>) => {
const responseHeader = getResponseHeaderObject(res.header)
const lowerCaseValue = responseHeader.getString("content-type") ?? ""
const upperCaseValue = responseHeader.getString("Content-Type")
data.jest_result = lowerCaseValue == "application/json" && upperCaseValue == null
},
fail: () => {
data.jest_result = false
},
})
}
defineExpose({
data,
sendArrayBuffer,
jest_request,
jest_set_cookie,
jest_set_cookie_expires,
jest_delete_cookie,
jest_cookie_request,
jest_timeout_null,
jest_get_with_data,
jest_get_with_generics,
jest_get_array,
jest_uts_module_invoked,
jest_respone_json_string,
jest_respone_with_string_generics,
jest_respone_with_404_and_string_generics,
jest_issue_19687,
jest_test_issue21823_crash,
jest_check_header_lowercase,
sendGzipRequest,
checkQuery,
checkHeaderLowerCase
})
</script>
<style>
.uni-textarea {
padding: 9px;
font-size: 14px;
}
</style>

查看文件

@@ -0,0 +1,80 @@
const platformInfo = process.env.uniTestPlatformInfo.toLocaleLowerCase()
const isMP = platformInfo.startsWith('mp')
describe('ExtApi-RequestTask', () => {
if ( isMP) {
it('skip', async () => {
expect(1).toBe(1);
});
return;
}
const PAGE_PATH = '/pages/API/request/requestTask'
let page;
let res;
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor('view');
});
it('Check RequestTask related api', async () => {
const onHeadersReceived_observe_1_btn = await page.$('#onHeadersReceived_observe_1')
await onHeadersReceived_observe_1_btn.tap()
const onHeadersReceived_observe_2_btn = await page.$('#onHeadersReceived_observe_2')
await onHeadersReceived_observe_2_btn.tap()
const onChunkReceived_observe_1_btn = await page.$('#onChunkReceived_observe_1')
await onChunkReceived_observe_1_btn.tap()
const onChunkReceived_observe_2_btn = await page.$('#onChunkReceived_observe_2')
await onChunkReceived_observe_2_btn.tap()
const checkRequestTask_btn = await page.$('#checkRequestTask')
await checkRequestTask_btn.tap()
await page.waitFor(8000);
let jest_requestTask_result = await page.data('jest_requestTask_result')
if (jest_requestTask_result) {
let res = await page.data('res')
let checkSuccess = res.includes('onHeadersReceived监听1') && res.includes('onHeadersReceived监听2') && res
.includes('onChunkReceived监听1') && res.includes('onChunkReceived监听2')
expect(checkSuccess).toBe(true)
const offHeadersReceived_id_btn = await page.$('#offHeadersReceived_id')
await offHeadersReceived_id_btn.tap()
await checkRequestTask_btn.tap()
await page.waitFor(8000);
jest_requestTask_result = await page.data('jest_requestTask_result')
if (jest_requestTask_result) {
res = await page.data('res')
checkSuccess = res.includes('onHeadersReceived监听2') == false && res.includes('onHeadersReceived监听1')
expect(checkSuccess).toBe(true)
}
const offChunkReceived_observe_btn = await page.$('#offChunkReceived_observe')
await offChunkReceived_observe_btn.tap()
const offHeadersReceived_observe_btn = await page.$('#offHeadersReceived_observe')
await offHeadersReceived_observe_btn.tap()
await checkRequestTask_btn.tap()
await page.waitFor(8000);
jest_requestTask_result = await page.data('jest_requestTask_result')
if (jest_requestTask_result) {
res = await page.data('res')
checkSuccess = res.includes('点击了 offHeadersReceived') && res.includes('点击了 offChunkReceived')
expect(checkSuccess).toBe(true)
}
}
})
});

查看文件

@@ -0,0 +1,186 @@
<template>
<view style="flex: 1;">
<view class="uni-padding-wrap uni-common-mt">
<view class="uni-common-mt" style="border-width: 2px;border-style: solid; border-radius: 4px;">
<textarea :value="res" class="uni-textarea" style="width: 100%"></textarea>
</view>
<view>
<text>地址 : {{ host + url}}</text>
<text>请求方式 : {{method}}</text>
</view>
<view class="uni-btn-v uni-common-mt">
<button type="primary" @tap="checkRequestTask" id="checkRequestTask">发起流式请求(设置监听需重新点击、勿重复点击)</button>
<view class="uni-common-pb"></view>
<button type="primary" @tap="abort" id="abort">中断流式请求</button>
</view>
</view>
<scroll-view style="flex: 1;" show-scrollbar="true">
<view style="padding: 20px;">
<text>添加或者移除流式监听</text>
<view class="uni-common-pb"></view>
<view style="flex-direction: row;flex-wrap: wrap;">
<button style="padding: 5px;" type="primary" size="mini" @tap="onHeadersReceived_observe_1"
id="onHeadersReceived_observe_1">onHeadersReceived监听1</button>
<button style="padding: 5px;" type="primary" size="mini" @tap="onHeadersReceived_observe_2"
id="onHeadersReceived_observe_2">onHeadersReceived监听2</button>
<button style="padding: 5px;" type="primary" size="mini" @tap="onChunkReceived_observe_1"
id="onChunkReceived_observe_1">onChunkReceived监听1</button>
<button style="padding: 5px;" type="primary" size="mini" @tap="onChunkReceived_observe_2"
id="onChunkReceived_observe_2">onChunkReceived监听2</button>
<button style="padding: 5px;" type="primary" size="mini" @tap="offHeadersReceived_id"
id="offHeadersReceived_id">offHeadersReceived(id)</button>
<button style="padding: 5px;" type="primary" size="mini" @tap="offHeadersReceived_observe"
id="offHeadersReceived_observe">offHeadersReceived移除所有</button>
<button style="padding: 5px;" type="primary" size="mini" @tap="offChunkReceived_observe"
id="offChunkReceived_observe">offChunkReceived移除所有</button>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const res = ref('')
const task = ref<RequestTask | null>(null)
const host = "https://request.dcloud.net.cn"
const url = "/api/http/contentType/eventStream?limit=10"
const method: RequestMethod = "POST"
const onHeadersReceivedObseves = ref<number[]>([])
const onChunkReceivedObseves = ref<number[]>([])
const onHeadersReceived_returnid_1 = ref(-1)
const onHeadersReceived_returnid_2 = ref(-1)
const isAbort = ref(false)
onLoad((options : OnLoadOptions) => {
onHeadersReceivedObseves.value.push(1)
onChunkReceivedObseves.value.push(1)
})
onUnload(() => {
uni.hideLoading();
task.value?.abort();
})
function onHeadersReceived_observe_1() {
onHeadersReceivedObseves.value.push(1)
}
function onHeadersReceived_observe_2() {
onHeadersReceivedObseves.value.push(2)
}
function offHeadersReceived_observe() {
onHeadersReceivedObseves.value = []
}
function offHeadersReceived_id() {
onHeadersReceivedObseves.value = []
onHeadersReceivedObseves.value.push(1)
}
function onChunkReceived_observe_1() {
onChunkReceivedObseves.value.push(1)
}
function onChunkReceived_observe_2() {
onChunkReceivedObseves.value.push(2)
}
function offChunkReceived_observe() {
onChunkReceivedObseves.value = []
}
function checkRequestTask() {
isAbort.value = false
res.value = '发起post流式请求 \n\n'
task.value = uni.request({
url: "https://request.dcloud.net.cn/api/http/contentType/eventStream?limit=10",
timeout: 600000,
method: method,
enableChunked: true,
// #ifndef APP-ANDROID && UNI-APP-X && !VUE3-VAPOR
dataType: 'text',
// #endif
success: (response) => {
console.log('request success', JSON.stringify(response.data))
console.log('request success header is :', JSON.stringify(response.header))
res.value += '流式请求结束 \n\n'
console.log('请求结果 : ' + JSON.stringify(response))
},
fail: (err) => {
let content = err.errMsg
if (isAbort.value) {
content = "中断成功"
}
console.log('request fail', err);
uni.showModal({
content: content,
showCancel: false
});
},
complete: () => {
task.value = null
},
});
const onHeadersReceivedCallback1 = (response : RequestTaskOnHeadersReceivedListenerResult) => {
console.log("-------onHeadersReceived监听1------", response)
res.value += `onHeadersReceived监听1:\n ${JSON.stringify(response)} \n\n`
}
const onHeadersReceivedCallback2 = (response : RequestTaskOnHeadersReceivedListenerResult) => {
console.log("-------onHeadersReceived监听2------", response)
res.value += `onHeadersReceived监听2:\n ${JSON.stringify(response)} \n\n`
}
if (onHeadersReceivedObseves.value.includes(1)) {
onHeadersReceived_returnid_1.value = task.value?.onHeadersReceived(onHeadersReceivedCallback1) ?? -1
}
if (onHeadersReceivedObseves.value.length == 0) {
task.value?.offHeadersReceived(null)
res.value += "点击了 offHeadersReceived \n\n"
} else if (onHeadersReceivedObseves.value.length == 1) {
task.value?.offHeadersReceived(onHeadersReceived_returnid_2.value)
}
const onChunkReceivedCallback1 = (response : RequestTaskOnChunkReceivedListenerResult) => {
const chunkText : string = new TextDecoder().decode(response.data)
console.log("-------onChunkReceived监听1------", chunkText)
res.value += `onChunkReceived监听1:\n ${chunkText}`
}
const onChunkReceivedCallback2 = (response : RequestTaskOnChunkReceivedListenerResult) => {
const chunkText : string = new TextDecoder().decode(response.data)
console.log("-------onChunkReceived监听2------", chunkText)
res.value += `onChunkReceived监听2:\n ${chunkText}`
}
if (onHeadersReceivedObseves.value.includes(2)) {
onHeadersReceived_returnid_2.value = task.value?.onHeadersReceived(onHeadersReceivedCallback2) ?? -1
}
if (onChunkReceivedObseves.value.includes(1)) {
task.value?.onChunkReceived(onChunkReceivedCallback1)
}
if (onChunkReceivedObseves.value.includes(2)) {
task.value?.onChunkReceived(onChunkReceivedCallback2)
}
if (onChunkReceivedObseves.value.length == 0) {
task.value?.offChunkReceived(null)
res.value += "点击了 offChunkReceived \n\n"
}
}
function abort() {
isAbort.value = true
task.value?.abort()
}
</script>
<style>
.uni-textarea {
padding: 9px;
font-size: 14px;
}
</style>