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

Zephyr Squad REST API导出Cycle为CSV时提示projectId参数缺失

调用Zephyr Squad Cycle导出API时提示"Missing parameter: projectId"的解决方法

问题分析

你遇到的核心问题是:Zephyr Squad的Cycle导出API(GET请求)要求参数通过URL查询参数传递,但你错误地将参数放在了JSON请求体中。GET请求规范通常不支持携带请求体,服务端无法识别你传入的projectId参数,因此报错。另外你生成的CANONICAL_PATH虽然包含查询参数,但实际请求并未在URL中带上这些参数,导致签名验证与实际请求不匹配,进一步引发参数校验失败。

解决方案

需要做以下几点修改:

  • 将请求参数从JSON请求体移到URL查询参数中传递
  • 确保CANONICAL_PATH的生成与实际请求的查询参数完全一致(包括参数顺序,因为QSH签名基于该路径生成)
  • 使用requests.get的params参数传递查询参数,而非json参数

修改后的完整代码

import json
import jwt
import time
import hashlib
import requests


def is_json(data):
    try:
        json.loads(data)
    except ValueError:
        return False
    return True

# USER
ACCOUNT_ID = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'

# ACCESS KEY from navigation >> Tests >> API Keys
ACCESS_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

# SECRET KEY from navigation >> Tests >> API Keys
SECRET_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

# JWT EXPIRE how long token been to be active? 3600 == 1 hour
JWT_EXPIRE = 3600

# BASE URL for Zephyr for Jira Cloud
BASE_URL = 'https://prod-api.zephyr4jiracloud.com/connect'

# RELATIVE PATH for token generation and make request to api
cycle_id = 'ca55798e-e9e8-4ebb-8b43-efac9360e615'

RELATIVE_PATH = '/public/rest/api/1.0/cycle/{}/export'.format(cycle_id)

# 定义查询参数,注意参数顺序要和CANONICAL_PATH中的一致
query_params = {
    'projectId': '10000',
    'versionId': '10000',
    'exportType': 'CSV',
    'folderId': 'UI'
}

# 构建CANONICAL_PATH:按照参数顺序拼接成查询字符串
query_str = '&'.join([f"{k}={v}" for k, v in query_params.items()])
CANONICAL_PATH = f'GET&{RELATIVE_PATH[1:]}?{query_str}'


# TOKEN HEADER: to generate jwt token
payload_token = {
            'sub': ACCOUNT_ID,
            'qsh': hashlib.sha256(CANONICAL_PATH.encode('utf-8')).hexdigest(),
            'iss': ACCESS_KEY,
            'exp': int(time.time())+JWT_EXPIRE,
            'iat': int(time.time())
        }

# GENERATE TOKEN
token = jwt.encode(payload_token, SECRET_KEY, algorithm='HS256').strip()

# REQUEST HEADER: to authenticate and authorize api
headers = {
            'Authorization': 'JWT '+token,
            'zapiAccessKey': ACCESS_KEY
        }

# MAKE REQUEST: 使用params传递查询参数,不再传json
raw_result = requests.get(BASE_URL + RELATIVE_PATH, headers=headers, params=query_params)
print(raw_result)

# Download the CSV file and save it to disk
with open("Export.csv", "w", newline="", encoding='utf-8') as csvfile:
    csvfile.write(raw_result.text)

if is_json(raw_result.text):
    # JSON RESPONSE: convert response to JSON
    json_result = json.loads(raw_result.text)
    # PRINT RESPONSE: pretty print with 4 indent
    print(json.dumps(json_result, indent=4, sort_keys=True))
else:
    print(raw_result.text)

关键修改点说明

  1. 参数传递方式:用params参数替代json参数,将所有请求参数放在URL查询参数中,符合GET请求规范,也匹配Zephyr API的要求。
  2. CANONICAL_PATH一致性:确保生成的规范路径和实际请求的URL查询参数完全一致,包括参数顺序,这样JWT签名的QSH(Query String Hash)才能通过验证。
  3. 优化文件保存:添加encoding='utf-8'避免CSV文件出现乱码问题。

内容的提问来源于stack exchange,提问作者Nɪsʜᴀɴᴛʜ ॐ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 21:25:04