问题:
在JS Action中使用 ctx.request 进行第三方API请求的时候, 通过headers中传递 Authorization参数, 实际请求时被替换成系统token值,导致 请求失败.
第三方API 与当前系统的域名同一主域名下的不同子域名(a.domain.com 和b.domain.com), 我看手册文档中说: 认证:同域请求会自动携带当前用户的 Token、locale、role;跨域需目标支持 CORS,并按需在 headers 中传入 token。
但实际这两方域名并不同域.
版本: v2.0.11
请求验证解决.
const res = await ctx.request({
url: 'https://api.example.com/v1/data',
method: 'get',
});
const res2 = await ctx.request({
url: 'https://api.other.com/items',
method: 'get',
headers: {
Authorization: 'Bearer <目标服务的 token>',
},
});
这里实测 Authorization 会被系统强制覆盖,无法完成第三方请求API的鉴权逻辑.
只能回归原始请求,自己造一个axios 进行请求了.
const axios = await ctx.importAsync('axios')
const instance = new axios.Axios({
responseType :'json'
});
const response = await instance.post()
JS Action 实现一个简单的请求第三方API接口, 并从接口响应中得到一个附件下载地址实现附件下载的功能,折腾了半天. 还是绕了一圈搞定了. 给各位一个参考:
// 1. 动态导入 axios 并获取 run_id
const A = await ctx.importAsync('axios');
const axios = new A.Axios();
const runId = ctx.record?.run_id";
if (!runId) {
ctx.message.error(ctx.t('当前记录未包含任务ID (run_id)'));
return;
}
// 显示加载状态
const hideLoading = ctx.message.loading(ctx.t('正在下载任务结果,请稍候...'), 10);
try {
// 2. 使用原始 axios 确保 Authorization Header 不被干扰
const response = await axios.post(
"https://xxxxxxxxxxxxxxxxxx/v1/workflows/run",
JSON.stringify({
"inputs": {
"run_id": String(runId)
},
"response_mode": "blocking",
"user": "nocobase"
}),
{
headers: {
'Authorization': 'Bearer app-xxxxxxxxxxxxxxxxxxx',
'Content-Type': 'application/json'
}
}
);
if (typeof hideLoading === 'function') hideLoading();
// 3. 解析文件列表
const data = typeof response.data==='string'?JSON.parse(response.data):response.data;
const files = data.data?.outputs?.files || [];
if (files.length === 0) {
ctx.message.warning(ctx.t('请求成功,但未发现可下载的文件'));
return;
}
// 4. UI 渲染:由于无法使用 document.createElement,通过弹窗列表展示下载按钮
const { React, antd } = ctx.libs;
const { List, Button, Typography } = antd;
const { DownloadOutlined, FileOutlined } = ctx.libs.antdIcons;
ctx.viewer.drawer({
title: ctx.t('文件列表 ({{count}})', { count: files.length }),
width: 450,
content: (
<div style={{ padding: '20px' }}>
<List
itemLayout="horizontal"
dataSource={files}
renderItem={(file) => (
<List.Item
actions={[
<Button
type="primary"
icon={<DownloadOutlined />}
href={file.url}
target="_blank" // 关键:在新窗口打开/触发浏览器原生下载,无需 document 操作
key="download"
>
{ctx.t('下载')}
</Button>
]}
>
<List.Item.Meta
avatar={<FileOutlined style={{ fontSize: '20px', color: '#1677ff' }} />}
title={file.filename || 'downloaded_file'}
description={
<Typography.Text type="secondary" ellipsis={{ tooltip: file.url }} style={{ maxWidth: '200px' }}>
{file.url}
</Typography.Text>
}
/>
</List.Item>
)}
/>
</div>
),
});
ctx.message.success(ctx.t('成功提取文件列表'));
} catch (e) {
if (typeof hideLoading === 'function') hideLoading();
ctx.message.error(ctx.t('接口请求失败: {{message}}', { message: e.message }));
}