feat: api 的 requestAdaptor 支持拦截请求 (#7566)

This commit is contained in:
liaoxuezhi 2023-07-25 14:31:19 +08:00 committed by GitHub
parent 4f07627051
commit 73a78a1406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 139 additions and 6 deletions

View File

@ -155,6 +155,8 @@ let amisScoped = amis.embed(
// 另外在 amis 配置项中的 api 也可以配置适配器,针对某个特定接口单独处理。 // 另外在 amis 配置项中的 api 也可以配置适配器,针对某个特定接口单独处理。
// //
// requestAdaptor(api) { // requestAdaptor(api) {
// // 支持异步,可以通过 api.mockResponse 来设置返回结果,跳过真正的请求发送
// // 此功能自定义 fetcher 的话会失效
// return api; // return api;
// } // }
// //

View File

@ -659,6 +659,7 @@ const schema = {
url: '/api/mock2/form/saveForm', url: '/api/mock2/form/saveForm',
requestAdaptor: function (api, context) { requestAdaptor: function (api, context) {
console.log(context); // 打印上下文数据 console.log(context); // 打印上下文数据
return { return {
...api, ...api,
data: { data: {
@ -687,6 +688,47 @@ const schema = {
你也可以使用`debugger`自行进行调试。 你也可以使用`debugger`自行进行调试。
#### 拦截请求
如果 api 发送适配器中,修改 api 对象,在 api 对象里面放入 `mockReponse` 属性则会拦截请求发送amis 内部会直接使用 `mockReponse` 的结果返回。
```js
const schema = {
type: 'form',
api: {
method: 'post',
url: '/api/mock2/form/saveForm',
requestAdaptor: function (api, context) {
return {
// 模拟 http 请求返回
mockResponse: {
status: 200, // http 返回状态
data: {
// http 返回结果
status: 0, // amis 返回数据的状态
data: {
name: '模拟返回的值'
}
}
}
};
}
},
body: [
{
type: 'input-text',
name: 'name',
label: '姓名:'
},
{
name: 'text',
type: 'input-email',
label: '邮箱:'
}
]
};
```
### 配置接收适配器 ### 配置接收适配器
同样的,如果后端返回的响应结构不符合 amis 的[接口格式要求](#%E6%8E%A5%E5%8F%A3%E8%BF%94%E5%9B%9E%E6%A0%BC%E5%BC%8F-%E9%87%8D%E8%A6%81-),而后端不方便调整时,可以配置`adaptor`实现接收适配器 同样的,如果后端返回的响应结构不符合 amis 的[接口格式要求](#%E6%8E%A5%E5%8F%A3%E8%BF%94%E5%9B%9E%E6%A0%BC%E5%BC%8F-%E9%87%8D%E8%A6%81-),而后端不方便调整时,可以配置`adaptor`实现接收适配器

View File

@ -51,12 +51,12 @@ export function embed(
container.classList.add('amis-scope'); container.classList.add('amis-scope');
let scoped = {}; let scoped = {};
const requestAdaptor = (config: any) => { const requestAdaptor = async (config: any) => {
const fn = const fn =
env && typeof env.requestAdaptor === 'function' env && typeof env.requestAdaptor === 'function'
? env.requestAdaptor.bind() ? env.requestAdaptor.bind()
: (config: any) => config; : async (config: any) => config;
const request = fn(config) || config; const request = (await fn(config)) || config;
return request; return request;
}; };
@ -189,7 +189,7 @@ export function embed(
config.method = method; config.method = method;
config.data = data; config.data = data;
config = requestAdaptor(config); config = await requestAdaptor(config);
if (method === 'get' && data) { if (method === 'get' && data) {
config.params = data; config.params = data;
@ -210,7 +210,9 @@ export function embed(
return true; return true;
}; };
let response = await axios(config); let response = config.mockResponse
? config.mockResponse
: await axios(config);
response = await attachmentAdpator(response, __); response = await attachmentAdpator(response, __);
response = responseAdaptor(api)(response); response = responseAdaptor(api)(response);

View File

@ -217,6 +217,7 @@ export interface ApiObject extends BaseApiObject {
operationName?: string; operationName?: string;
body?: PlainObject; body?: PlainObject;
query?: PlainObject; query?: PlainObject;
mockResponse?: PlainObject;
adaptor?: ( adaptor?: (
payload: object, payload: object,
response: fetcherResult, response: fetcherResult,

View File

@ -505,6 +505,12 @@ export function wrapFetcher(
api.headers['Content-Type'] = 'application/json'; api.headers['Content-Type'] = 'application/json';
} }
// 如果发送适配器中设置了 mockResponse
// 则直接跳过请求发送
if (api.mockResponse) {
return wrapAdaptor(Promise.resolve(api.mockResponse) as any, api, data);
}
if (!isValidApi(api.url)) { if (!isValidApi(api.url)) {
throw new Error(`invalid api url:${api.url}`); throw new Error(`invalid api url:${api.url}`);
} }

View File

@ -359,7 +359,7 @@ test('api:isvalidapi', () => {
).toBeTruthy(); ).toBeTruthy();
}); });
test('api:requestAdaptor', async () => { test('api:requestAdaptor1', async () => {
const notify = jest.fn(); const notify = jest.fn();
const fetcher = jest.fn().mockImplementation(() => const fetcher = jest.fn().mockImplementation(() =>
Promise.resolve({ Promise.resolve({
@ -429,3 +429,83 @@ test('api:requestAdaptor', async () => {
email: 'appended@test.com' email: 'appended@test.com'
}); });
}); });
test('api:requestAdaptor2', async () => {
const notify = jest.fn();
const fetcher = jest.fn().mockImplementation(() =>
Promise.resolve({
data: {
status: 0,
msg: 'ok',
data: {
id: 1
}
}
})
);
const requestAdaptor = jest.fn().mockImplementation(api => {
return Promise.resolve({
...api,
mockResponse: {
status: 200,
data: {
status: 0,
msg: 'ok',
data: {
id: 2
}
}
}
});
});
const {container, getByText} = render(
amisRender(
{
type: 'page',
body: [
{
type: 'form',
id: 'form_submit',
submitText: '提交表单',
api: {
method: 'post',
url: '/api/mock2/form/saveForm',
requestAdaptor: requestAdaptor
},
body: [
{
type: 'input-text',
name: 'id',
label: 'Id'
},
{
type: 'input-text',
name: 'name',
label: '姓名:',
value: 'fex'
}
]
}
]
},
{},
makeEnv({
notify,
fetcher
})
)
);
await waitFor(() => {
expect(getByText('提交表单')).toBeInTheDocument();
});
fireEvent.click(getByText(/提交表单/));
await wait(300);
expect(requestAdaptor).toHaveBeenCalled();
expect(fetcher).toHaveBeenCalledTimes(0);
expect(container.querySelector('input[name="id"]')).toBeInTheDocument();
expect((container.querySelector('input[name="id"]') as any).value).toBe('2');
});