Request Tracker 6.0.3 REST API 实用指南
REST 2.0 与 REST 1.0 全端点实测(RT 6.0.3):Auth Token 认证、Apache 反代丢 Authorization 头与 REST2 建单 body 格式两大生产坑排障,附 rt CLI 与 rt-crontool 定时批处理实测。
Request Tracker 6.0.3 REST API 实用指南
RT 提供三代程序化接口:REST 2.0(JSON,官方主推)、REST 1.0(邮件格式,广泛存量)、以及封装 REST 1.0 的命令行工具
rt。本文在《Request Tracker 6.0.3 安装指南》部署的实例上全量实测:认证方式(token / session cookie)、10+ 个端点的请求格式、以及两个非常有价值的生产坑(Apache 反代丢 Authorization 头、REST2 建单 body 格式)。实测日期:2026-10-24。
一、API 全景与选择建议
| 接口 | 路径 | 数据格式 | 认证 | 状态 |
|---|---|---|---|---|
| REST 2.0 | /REST/2.0/... | JSON | Auth Token(Authorization: token)或会话 Cookie | 主推(RT 4.4+) |
| REST 1.0 | /REST/1.0/... | 类邮件头文本 | 会话 Cookie(表单登录)或 Basic(视配置) | 存量兼容 |
| rt CLI | /opt/rt6/bin/rt | 命令行 | ~/.rtrc(内部走 REST 1.0) | 与系统同生命周期 |
| rt-crontool | /opt/rt6/sbin/rt-crontool | 命令行 | 本地执行 | 定时批处理 |
选择建议:新集成一律 REST 2.0(JSON、超媒体 _url 自描述、语义化 HTTP 状态码);维护老脚本用 REST 1.0;运维批量操作直接 rt CLI + rt-crontool 组合。
二、REST 2.0 实测
2.1 认证:Auth Token(实测推荐)
REST 2.0 的官方推荐认证是用户级 Auth Token(不是密码),生成入口:Web 登录 → 右上角 Logged in as root → Settings → About me(或 Preferences)→ Auth Tokens → New Token,填描述并输入自己的登录密码确认,token 只显示一次,格式 1-14-xxxx(用户 ID 开头)。
调用时附头(格式官方文档明确要求 Authorization: token <token>,中间的 token 一词不能省):
curl -H 'Authorization: token 1-14-85667bbb02f060bfb16da2d41221d05b' \
'http://127.0.0.1/REST/2.0/ticket/1'
实测返回(JSON,节选):
{
"Queue" : { "type" : "queue", "id" : "1",
"_url" : "http://rt-server.example.com/REST/2.0/queue/1" },
"Subject" : "CLI test ticket",
"Status" : "open",
"Requestors" : [ { "_url" : ".../REST/2.0/user/alice@example.com", ... } ],
"Created" : "2026-10-24T05:59:54Z"
}
带认证失败时返回 {"message":"Unauthorized"}(HTTP 401)——实测 token 错误或(见 2.2)头没到后端都是这个表现。
2.2 生产坑 #1:Apache 反代下 Authorization 头丢失(实测排障)
- 现象:直连后端一切正常,经 Apache
mod_proxy_fcgi反代后 REST2 全部 401{"message":"Unauthorized"};同一个 token、同一个头格式。 - 诊断过程:在本机另起一个独立 HTTP 实例(
/opt/rt6/sbin/rt-server --port 8898)绕过 Apache,同样的 curl 命令立即返回 200 JSON——证明 token 有效,是 Apache 层把 Authorization 头吞了(Apache 处理到 mod_proxy_fcgi 时默认不把 Authorization 透传给 FastCGI 后端环境变量)。 - 解法(Apache 2.4.52 实测可用):
实测加上后 REST2 token 认证全通。
RewriteEngine On RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] - 为什么不用现成指令:新版本 Apache 提供
CGIPassAuth On/ProxyFCGISetIf "true" HTTP_AUTHORIZATION "%{Authorization}s",但Ubuntu 22.04 的 Apache 2.4.52 尚无这两个指令(实测 configtest 直接报 Invalid command,apache 起不来),所以用 mod_rewrite 环境变量方案最稳。FCGI 协议会把HTTP_AUTHORIZATION环境变量作为 Authorization 头交给后端。 - 提醒:若你的 Apache ≥ 2.4.62,优先用
ProxyFCGISetIf;≥ 2.4.62 也可用CGIPassAuth On。
2.3 端点实测清单(全部实测通过)
以下实例均实测通过(T="Authorization: token <token>"):
① 查看单工单:
curl -s -H "$T" http://127.0.0.1/REST/2.0/ticket/1
# 实测:HTTP 200,返回完整 JSON(含 _url 超媒体链接)
② 建单(官方格式:字段顶层平铺):
curl -s -H "$T" -X POST http://127.0.0.1/REST/2.0/ticket \
-H 'Content-Type: application/json' \
--data '{"Queue":"General","Subject":"created via REST2","Requestor":["bob@example.com"]}'
# 实测输出:
# {"id":"3","_url":"http://rt-server.example.com/REST/2.0/ticket/3","type":"ticket"}
# HTTP 201 Created(响应头含 Location 指向新工单)
⚠ 生产坑 #2:建单 body 不能包 {"Fields":{...}}。实测把字段包进 Fields 会稳定返回 {"message":"Could not create ticket. Queue not set"}(HTTP 400)——REST2 的 validate_input 在反序列化后的顶层找 Queue,包了 Fields 顶层就只有 Fields 一个键。源码依据:lib/RT/REST2/Resource/Ticket.pm 的 validate_input 与 lib/RT/REST2/Resource/Record/Writable.pm 的 from_json(直接 decode_json($request->content) 取顶层)。PUT 更新同理用顶层平铺。
③ 更新工单:
curl -s -H "$T" -X PUT http://127.0.0.1/REST/2.0/ticket/1 \
-H 'Content-Type: application/json' \
--data '{"Status":"open"}'
# 实测输出:[] (空 JSON 数组 = 更新成功且无警告;失败时返回错误对象)
④ 评论(comment,内部备注):
curl -s -H "$T" -X POST http://127.0.0.1/REST/2.0/ticket/1/comment \
-H 'Content-Type: application/json' \
--data '{"Content":"comment via REST2","ContentType":"text/plain"}'
# 实测输出:[ "Comments added" ]
⑤ 回复(correspond,发请求人):
curl -s -H "$T" -X POST http://127.0.0.1/REST/2.0/ticket/1/correspond \
-H 'Content-Type: application/json' \
--data '{"Content":"reply via REST2","ContentType":"text/plain"}'
# 实测输出:[ "Correspondence added" ]
⑥ 历史事务(分页):
curl -s -H "$T" http://127.0.0.1/REST/2.0/ticket/1/history
# 实测输出(节选):
# { "per_page" : 20, "count" : 20, "pages" : 2,
# "next_page" : "http://127.0.0.1/REST/2.0/ticket/1/history?page=2",
# "items" : [ { "id" : "42", "type" : "transaction", "_url" : ".../transaction/42" }, ... ] }
# 取单条事务:GET /REST/2.0/transaction/42
⑦ 搜索工单:
curl -s -H "$T" "http://127.0.0.1/REST/2.0/tickets?query=Queue%3D%27General%27"
# 实测输出:{ "count" : n, "items" : [ { "id" : "1", "type" : "ticket", "_url" : ".../ticket/1" }, ... ] }
# 可加参数:orderby=Created&fields=Subject,Status (fields 控制返回字段)
⑧ 队列列表:
curl -s -H "$T" http://127.0.0.1/REST/2.0/queues/all
# 实测输出:{ "total" : 1, "items" : [ { "id" : "1", "type" : "queue",
# "_url" : ".../REST/2.0/queue/1" } ] }
# 单队列详情:GET /REST/2.0/queue/1
⑨ 用户对象:
curl -s -H "$T" http://127.0.0.1/REST/2.0/user/root
# 实测输出:{ "_hyperlinks" : [ { "type" : "user", "ref" : "self", "id" : "14", ... } ], ... }
⑩ 认领/取回:POST /REST/2.0/ticket/1/take(另有 untake/steal,dispatch 规则同源码 82 行)。
REST2 响应的特征(实测总结):
- 所有对象间引用都是
_url超媒体链接(JSON HyperSchema 风格),顺着_url就能走完对象图。 - 集合端点统一
{ count, per_page, pages, items }信封;/tickets与/queues/all等"/all"变体区别在于是否强制包含禁用对象。 - 写操作成功返回
[]或操作结果数组;失败返回{"message": "..."}+ 4xx。 - 注意区分单复数端点:
/ticket/1(单条,可写)与/tickets(搜索集合,POST 只作 GET 用——源码RT::REST2::Resource::Tickets带ProcessPOSTasGET角色)。想建单必须 POST 单数/ticket。 - RT 管理员可在配置中关停 REST2:
Set($EnableREST2, undef);(默认开启)。
2.4 用会话 Cookie 调 REST 2.0
token 之外,REST2 也接受浏览器会话(先表单登录拿 Cookie):
JAR=/tmp/rt_cookies.txt
NEXT=$(curl -s -c $JAR http://127.0.0.1/ | grep -oE 'name="next" value="[^"]*"' | sed 's/.*value="//;s/"$//')
curl -s -b $JAR -c $JAR http://127.0.0.1/NoAuth/Login.html \
--data-urlencode user=root --data-urlencode pass=你的密码 --data-urlencode next=$NEXT
curl -s -b $JAR http://127.0.0.1/REST/2.0/ticket/1 # 实测:200 JSON
实测要点:RT 6 登录表单带 next nonce 字段;登录后 CSRFToken 校验只拦 HTML 表单 POST,不影响 REST GET。适合人机混合脚本,服务端集成仍推荐 token。
三、REST 1.0 实测
REST 1.0 无 token 机制,认证即"登录一个会话":
JAR=/tmp/r1.txt
NEXT=$(curl -s -c $JAR http://127.0.0.1/ | grep -oE 'name="next" value="[^"]*"' | sed 's/.*value="//;s/"$//')
curl -s -b $JAR -c $JAR http://127.0.0.1/NoAuth/Login.html \
--data-urlencode user=root --data-urlencode pass=你的密码 --data-urlencode next=$NEXT
实测通过的端点(响应第一行固定 RT/6.0.3 200 Ok,正文为邮件头风格):
查工单:
curl -s -b $JAR http://127.0.0.1/REST/1.0/ticket/1/show
# 实测输出(节选):
# RT/6.0.3 200 Ok
#
# id: ticket/1
# Queue: General
# Owner: Nobody
# Subject: CLI test ticket
# Status: open
# Requestors: alice@example.com
搜索(参数名是 query,不是 qid——实测用错参数会报 no value sent for required parameter 'query'):
curl -s -b $JAR "http://127.0.0.1/REST/1.0/search/ticket?query=Queue%3D%27General%27"
# 实测输出:
# RT/6.0.3 200 Ok
#
# 1: CLI test ticket
# 3: created via REST2
建单(content 表单字段承载邮件风格指令):
curl -s -b $JAR http://127.0.0.1/REST/1.0/ticket/new \
--data-urlencode $'content=Queue: General\nSubject: created via REST1\nRequestor: carol@example.com\nText: body via REST1'
# 实测输出:
# RT/6.0.3 200 Ok
#
# # Ticket 4 created.
评论与改单(content 首行必须是 Action: comment / 直接字段):
curl -s -b $JAR http://127.0.0.1/REST/1.0/ticket/1/comment \
--data-urlencode $'content=Action: comment\nText: REST1 comment via curl'
# 实测输出:RT/6.0.3 200 Ok / # Comments added
# 注意:不带 Action 头时实测返回 400 Bad Request "# Invalid action: `'."
curl -s -b $JAR http://127.0.0.1/REST/1.0/ticket/1/edit \
--data-urlencode $'content=Status: resolved'
# 实测输出:RT/6.0.3 200 Ok / # Ticket 1 updated.
REST 1.0 的完整动词表(官方 rt.html 文档):ticket/new、ticket/<id>/show|edit|comment|correspond|history|links|merge、search/ticket、user/<id>、queue/<id>、attachment/<id> 等十余个。响应 RT/3.x.y <状态码> <消息> 开头是程序化解析的锚点。
四、rt CLI:REST 的命令行封装
/opt/rt6/bin/rt 内部走 REST 1.0,配置文件 ~/.rtrc(权限 600):
server http://127.0.0.1/
user root
passwd q1w2e3r4
实测命令集(每条均为实跑输出):
# 查看对象
rt show user/root # 实测输出 Name: root / EmailAddress: root@localhost / Privileged: 1 ...
rt show ticket/1 # 邮件头风格全字段
# 搜索(TicketSQL)
rt ls "Queue = 'General'" # 实测:1: CLI test ticket / 3: created via REST2
rt ls -f Subject,Status "Queue = 'General' AND Status = 'resolved'" # -f 控制输出列
# 建单
rt create -t ticket set "Subject=CLI test ticket" "Queue=General" \
"Requestor=alice@example.com" "Text=First ticket created via rt CLI"
# 实测输出:# Ticket 1 created.
# 改单
rt edit ticket/1 set Status=resolved # 实测输出:# Ticket 1 updated.
echo "Password: q1w2e3r4" | rt edit user/root set Password=q1w2e3r4
# 实测输出:# User root updated.
# 评论与回复
rt comment -m "internal note via CLI" ticket/1 # 实测:# Comments added
rt correspond -m "reply to requestor via CLI" ticket/1 # 实测:# Correspondence added
# 合并(注意:只接受数字 id,写 ticket/2 会报 Unrecognised argument,实测)
rt merge 2 1 # 实测输出:# Merge completed.
# 帮助
rt help # 总览;rt help list / show / create / edit / merge 分章
两个实测坑:
rt merge的参数是rt merge <from-id> <to-id>,只收数字;ticket/2形式会报Unrecognised argument 'ticket/2'。- 已 resolved 的工单再收到 correspond 会被重新打开(RT 默认生命周期行为)——本实验实测:resolve 后执行
rt correspond,工单状态自动回到 open。做自动化测试时别把这个当 bug。
五、rt-crontool:定时批处理
/opt/rt6/sbin/rt-crontool 是给 crontab 用的批处理引擎:搜索一批工单并对每张执行一个 Action。实测:
/opt/rt6/sbin/rt-crontool --search RT::Search::ActiveTickets \
--set RT::Action::RecordComment \
--template 'blank' \
--argument 'daily scan via crontool'
# 实测:无输出且工单历史出现 "daily scan via crontool" 评论(rt CLI show 可见)
三段式结构:--search(选工单,如 RT::Search::ActiveTickets / RT::Search::FromSQL 配 --search-arg "Status='open'")、--set(动作,如 RecordComment / Notify)、--template(通知模板)。典型 crontab:
# 每日 8 点给逾期未回工单加提醒评论
0 8 * * * /opt/rt6/sbin/rt-crontool --search RT::Search::FromSQL --search-arg "Status='open' AND LastUpdated < '2 days ago'" --set RT::Action::RecordComment --template 'blank' --argument 'Auto reminder: no update for 2 days'
# 会话清理(官方建议每周)
30 0 * * 0 /opt/rt6/sbin/rt-clean-sessions --older 8D
# 邮件摘要
0 9 * * * /opt/rt6/sbin/rt-email-digest -m daily
(rt-clean-sessions / rt-email-digest 为安装自带工具,官方推荐周期运行。)
六、排障速查表
| 现象 | 根因 | 解法(本篇实测章节) |
|---|---|---|
REST2 全部 401 {"message":"Unauthorized"},Web 界面正常 | Apache 反代丢 Authorization 头 | §2.2 mod_rewrite 环境变量转发(Apache 2.4.52 实测) |
apachectl configtest 报 Invalid command 'CGIPassAuth' / 'ProxyFCGISetIf' | Apache 2.4.52 无这两个指令 | 同上用 mod_rewrite(§2.2) |
REST2 建单 400 Could not create ticket. Queue not set | body 包了 {"Fields":{...}} | 字段顶层平铺(§2.3 ②) |
REST2 更新成功但返回 [] 看着像失败 | [] 就是成功语义(无警告) | 判定逻辑按 HTTP 状态码 + 数组(§2.3 ③) |
REST1 search 报 no value sent for required parameter 'query' | 参数名写成了 qid | 参数名 query(§三) |
REST1 comment 400 Invalid action | content 缺 Action: comment 首行 | 补 Action 头(§三) |
rt merge ticket/2 ticket/1 报 Unrecognised argument | merge 只收数字 id | rt merge 2 1(§四) |
rt CLI 报 Incorrect username or password | ~/.rtrc 里是旧密码,或 root 密码未初始化为约定值 | 更新 .rtrc;改密 rt edit user/root set Password=...(§四) |
七、参考链接
- REST 2.0 官方文档(本版随源码):https://docs.bestpractical.com/rt/6.0.3/RT_REST2.html
- REST 1.0 与 rt CLI 文档:https://docs.bestpractical.com/rt/6.0.3/rt.html
- TicketSQL 语法:https://docs.bestpractical.com/rt/6.0.3/searching.html
- 同系列:《Request Tracker 6.0.3 介绍与选型指南》《Request Tracker 6.0.3 安装指南》《Request Tracker 6.0.3 配置参数详解》