diff --git a/packages/amis-core/src/utils/api.ts b/packages/amis-core/src/utils/api.ts index aaa85cf3b..1fcf579f5 100644 --- a/packages/amis-core/src/utils/api.ts +++ b/packages/amis-core/src/utils/api.ts @@ -738,10 +738,25 @@ export function isApiOutdated( } export function isValidApi(api: string) { - return ( - api && - /^(?:(https?|wss?|taf):\/\/[^\/]+)?(\/?[^\s\/\?]*){1,}(\?.*)?$/.test(api) - ); + if (!api || typeof api !== 'string') { + return false; + } + const idx = api.indexOf('://'); + + // 不允许直接相对路径写 api + // 不允许 :// 结尾 + if ((!~idx && api[0] !== '/') || (~idx && idx + 3 === api.length)) { + return false; + } + + try { + // 不补一个协议,URL 判断为 false + api = (~idx ? '' : 'schema://domain') + api; + new URL(api); + } catch (error) { + return false; + } + return true; } export function isEffectiveApi( diff --git a/packages/amis/__tests__/event-action/ajax.test.tsx b/packages/amis/__tests__/event-action/ajax.test.tsx index 123443d0b..c3f144582 100644 --- a/packages/amis/__tests__/event-action/ajax.test.tsx +++ b/packages/amis/__tests__/event-action/ajax.test.tsx @@ -35,7 +35,7 @@ test('EventAction:ajax', async () => { actionType: 'ajax', args: { api: { - url: 'api/xxx', + url: '/api/xxx', method: 'get' }, messages: { @@ -71,7 +71,7 @@ test('EventAction:ajax', async () => { actionType: 'ajax', args: { api: { - url: 'api/xxx', + url: '/api/xxx', method: 'get' }, messages: { diff --git a/packages/amis/__tests__/event-action/custom.test.tsx b/packages/amis/__tests__/event-action/custom.test.tsx index 0c125fe4a..3027973c5 100644 --- a/packages/amis/__tests__/event-action/custom.test.tsx +++ b/packages/amis/__tests__/event-action/custom.test.tsx @@ -35,7 +35,7 @@ test('EventAction:custom', async () => { actionType: 'custom', args: { script: - "doAction({actionType: 'ajax', args: {api: 'api/xxx'}, outputVar: 'result'});" + "doAction({actionType: 'ajax', args: {api: '/api/xxx'}, outputVar: 'result'});" } }, { @@ -60,7 +60,7 @@ test('EventAction:custom', async () => { actionType: 'custom', args: { script: - "doAction({actionType: 'ajax', args: {api: 'api/xxx'}, outputVar: 'result'});event.stopPropagation();" + "doAction({actionType: 'ajax', args: {api: '/api/xxx'}, outputVar: 'result'});event.stopPropagation();" } }, { diff --git a/packages/amis/__tests__/event-action/prevent.test.tsx b/packages/amis/__tests__/event-action/prevent.test.tsx index 6c55f63bc..28278abbf 100644 --- a/packages/amis/__tests__/event-action/prevent.test.tsx +++ b/packages/amis/__tests__/event-action/prevent.test.tsx @@ -60,7 +60,7 @@ test('EventAction:prevent', async () => { actionType: 'ajax', args: { api: { - url: 'api/xxx', + url: '/api/xxx', method: 'get' } } diff --git a/packages/amis/__tests__/renderers/Form/__snapshots__/inputTable.test.tsx.snap b/packages/amis/__tests__/renderers/Form/__snapshots__/inputTable.test.tsx.snap index 912981347..17a636fb4 100644 --- a/packages/amis/__tests__/renderers/Form/__snapshots__/inputTable.test.tsx.snap +++ b/packages/amis/__tests__/renderers/Form/__snapshots__/inputTable.test.tsx.snap @@ -415,7 +415,6 @@ exports[`Renderer:input table add 1`] = ` >
@@ -1150,7 +1146,6 @@ exports[`Renderer: crud keepItemSelectionOnPageChange & maxKeepItemSelectionLeng
@@ -1963,7 +1952,6 @@ exports[`Renderer:crud basic interval headerToolbar footerToolbar 1`] = `
@@ -3815,7 +3796,6 @@ exports[`Renderer:crud source & alwaysShowPagination 1`] = `
@@ -278,7 +274,6 @@ exports[`Renderer:Pagination 1`] = `
@@ -391,7 +387,6 @@ exports[`Renderer:table 1`] = `
@@ -852,7 +842,6 @@ exports[`Renderer:table align 1`] = `
@@ -1517,7 +1497,6 @@ exports[`Renderer:table children 1`] = `
@@ -2053,7 +2022,6 @@ exports[`Renderer:table classNameExpr 1`] = `
@@ -2716,7 +2665,6 @@ exports[`Renderer:table column head style className 1`] = `
@@ -3353,7 +3293,6 @@ exports[`Renderer:table combine Renderer:table combineNum only 1`] = `
@@ -4125,7 +4053,6 @@ exports[`Renderer:table combine Renderer:table combineNum with fromIndex 1`] = `
@@ -5057,7 +4973,6 @@ exports[`Renderer:table groupName-default 1`] = ` colspan="2" data-index="3" rowspan="1" - style="width: 0px; height: 0px;" >
@@ -6022,7 +5924,6 @@ exports[`Renderer:table groupName-middleNoGroupName 1`] = ` colspan="1" data-index="3" rowspan="1" - style="width: 0px; height: 0px;" >
@@ -6984,7 +6872,6 @@ exports[`Renderer:table groupName-startNoGroupName 1`] = ` data-index="3" label="Engine" rowspan="2" - style="width: 0px; height: 0px;" >
@@ -7938,7 +7813,6 @@ exports[`Renderer:table groupName-withTpl 1`] = ` colspan="2" data-index="3" rowspan="1" - style="width: 0px; height: 0px;" >
@@ -8695,7 +8545,6 @@ exports[`Renderer:table isHead fixed 1`] = `
{ expect(buildApi('/api/xxx')).toMatchObject({ @@ -322,3 +322,22 @@ test('api:cache', async () => { expect(fetcher).toHaveBeenCalledTimes(1); // 只请求一次,第二次请求从缓存中取 expect(container).toMatchSnapshot(); }); + +test('api:isvalidapi', () => { + expect(isValidApi('api/xxx')).toBeFalsy(); + expect(isValidApi('api/xxx?a=1')).toBeFalsy(); + expect(isValidApi('/x')).toBeTruthy(); + expect(isValidApi('/api/xxx?a=1&b=2&c=3')).toBeTruthy(); + expect(isValidApi('http://xxxdomain')).toBeTruthy(); + expect(isValidApi('http://xxxdomain/')).toBeTruthy(); + expect(isValidApi('http://xxxdomain/api')).toBeTruthy(); + expect(isValidApi('app://')).toBeFalsy(); + expect(isValidApi('app://x')).toBeTruthy(); + expect(isValidApi('app://x?a=1')).toBeTruthy(); + expect(isValidApi('app://x?a=1&b=2')).toBeTruthy(); + expect(isValidApi('app://x b?a=1&b=2')).toBeFalsy(); + expect(isValidApi('app://x%20b?a=1&b=2')).toBeTruthy(); + expect(isValidApi('ftp://127.0.0.1/xxx')).toBeTruthy(); + expect(isValidApi('wss://127.0.0.1/xxx')).toBeTruthy(); + expect(isValidApi('taf://127.0.0.1/xxx')).toBeTruthy(); +}); diff --git a/packages/amis/src/renderers/Table/index.tsx b/packages/amis/src/renderers/Table/index.tsx index 6e72c2df2..5d9f5c82a 100644 --- a/packages/amis/src/renderers/Table/index.tsx +++ b/packages/amis/src/renderers/Table/index.tsx @@ -1221,6 +1221,11 @@ export default class Table extends React.Component { this.outterWidth = outter.offsetWidth; this.outterHeight = outter.offsetHeight; + // 没有渲染则跳过 + if (!this.totalWidth) { + return; + } + let widths: { [propName: string]: number; } = (this.widths = {});