You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Playwright apiContext中移除Authorization请求头?

问题描述

我将项目的Authorization实现为fixture,让所有测试模块都能以登录用户身份运行。现有一个S3文件上传功能,流程分为两步:

  • 初始化上传:返回S3的upload_url及其他信息;
  • 上传至S3:必须使用该upload_url,且不允许携带Authorization请求头。

但上传时出现报错:

return (<Code>InvalidArgument</Code>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified
)

我尝试在上传函数中将请求头设为空,但无效。请问该如何解决?是否必须使用browserContext并调用route()函数?

相关代码

测试函数代码

export const demandTags = async (
  apiContext,
  campaign,
  campaignLinesDetails: CampaignLineResponse[],
  demandTagsDetails: DemandTagRequest[],
  use: (r: DemandTagResponse[]) => Promise<void>,
) => {
  const results: DemandTagResponse[] = [];

  // Get uploadCreative.mp4 from the files
  const filePromise = new Promise((resolve, reject) => {
    readFile('./uploadItems/uploadCreative.mp4', (err, data) => {
      if (err) {
        reject(err);
      } else {
        resolve(data);
      }
    });
  });
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const file: any = await filePromise;

  // Initiate upload
  const initiateUpload = await apiContext.post('/api/demandtag/initiate_upload/', {
    data: {
      file_name: 'uploadCreative.mp4',
      file_type: 'video/mp4',
      file_size: file.length,
    } as apiTypes.UploadRequest,
  });
  await expect(initiateUpload).toBeOK();

  const responseJSONinitiateUpload = await initiateUpload.json();
  expect(responseJSONinitiateUpload.parts[0].offset).toBe(0);
  expect(responseJSONinitiateUpload.parts[0].limit).toBe(file.length);
  expect(responseJSONinitiateUpload.parts).toHaveLength(1);

  // Upload the part
  const partResponse = await apiContext.put(responseJSONinitiateUpload.parts[0].url, {
    headers: {
      // 'Content-Type': '',
    },
    data: file,
  });
  await expect(partResponse).toBeOK();

  const etag = await partResponse.headers()['etag'];
  const completeUpload = await apiContext.post('/api/demandtag/complete_upload/', {
    data: {
      upload_token: responseJSONinitiateUpload.upload_token,
      etags: [etag],
    } as apiTypes.CompleteUploadRequest,
  });
  await expect(completeUpload).toBeOK();

  const responseJSONcompleteUpload = await completeUpload.json();

  // Create demand tag
  for (const demandTagDetails of demandTagsDetails) {
    const createDemandTag = await apiContext.post('/api/demandtag/', {
      data: {
        name: demandTagDetails.name,
        budget_value: demandTagDetails.budgetValue,
        campaign_line: campaignLinesDetails[0].id,
        pixel_url: demandTagDetails.pixelUrl,
        creative_landing_page_url: demandTagDetails.creativeLandingPageUrl,
        creative_s3: responseJSONcompleteUpload.url,
        creative_slug: demandTagDetails.creativeSlug,
        start_date: demandTagDetails.startDate,
        end_date: demandTagDetails.endDate,
      } as apiTypes.DemandTagRequest,
    });

    await expect(createDemandTag).toBeOK();
    results.push({ ...demandTagDetails, id: (await createDemandTag.json()).id });
  }

  await use(results);

  // Delete created demand tags
  for (const demandTag of results) {
    const responseDelete = await apiContext.delete(`/api/demandtag/${demandTag.id}/`);
    expect([204, 404]).toContain(responseDelete.status());
  }
};

Fixture定义代码

export const test = base.extend<TestFixtures, WorkerFixtures>({
  authToken: async ({ request }, use) => {
    await authToken(request, use);
  },
  apiContext: async ({ authToken }, use) => {
    await use(
      await request.newContext({
        baseURL: `${process.env.PROTOCOL!}://${process.env.HOST!}:${process.env.PORT!}`,
        extraHTTPHeaders: {
          authorization: `Token ${authToken}`,
        },
      }),
    );
  },
  staticUrl: [
    async ({}, use) => {
      await staticUrlFixture(use);
    },
    { scope: 'worker' },
  ],
});

解决方案

不需要使用browserContext的route()函数,有两种更简单的解决方式:

方法一:显式移除Authorization头

当前的apiContext全局携带了Authorization头,直接设置headers为空字符串不会覆盖全局头,必须显式将authorization设为undefined,这样会移除全局的对应请求头:

修改上传部分的代码:

// Upload the part
const partResponse = await apiContext.put(responseJSONinitiateUpload.parts[0].url, {
  headers: {
    authorization: undefined, // 显式移除全局Authorization头
    'Content-Type': 'video/mp4', // 根据文件类型设置正确的Content-Type
  },
  data: file,
});
await expect(partResponse).toBeOK();

方法二:创建不带Auth头的独立API上下文

如果需要多次发起无Auth的请求,可以创建一个临时的、不带额外HTTP头的API上下文:

// 创建临时API上下文(无全局Auth头)
const s3ApiContext = await request.newContext();

// Upload the part
const partResponse = await s3ApiContext.put(responseJSONinitiateUpload.parts[0].url, {
  headers: {
    'Content-Type': 'video/mp4',
  },
  data: file,
});
await expect(partResponse).toBeOK();

// 可选:请求完成后销毁上下文
await s3ApiContext.dispose();

为什么之前设空无效?

Playwright的API上下文会合并全局extraHTTPHeaders和请求级的headers:

  • 如果请求级设置某个头为空字符串,全局的对应头仍会保留;
  • 只有将请求级的头设为undefined,才会完全移除该头。

内容的提问来源于stack exchange,提问作者jyang07

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 18:40:15