JS Action中 ctx.request 请求 Authorization 被系统覆盖BUG

问题:
在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 }));
}

感谢您反馈的缺陷,请等待我们修复它。