quickform.cn 发布与数据收集
> 一句话:让 AI 生成的网页,变成「学生点开就能做、你在后台能看到全班数据」的在线作业。
第 0 步:第一次用?照这四步走
> 这一节是给从没用过 quickform 的新用户的。Skill 在对话里会主动引导,你也可以直接把这一节转发给别人。
① 注册账号
打开 https://quickform.cn/register ,用邮箱注册并登录。
② 完成「教师认证」——这一步不能跳
没认证的话,接口一律返回 403 certification_required,什么都做不了。
- 入口:登录后进个人中心 / 个人设置
- 认证通过后你会看到这条绿条:
> 已认证教师:您的账号已完成教师身份认证,任务创建不再受限,上传的 HTML 页面将自动公开访问。 - 认证后的好处:建任务不限个数、上传的 HTML 自动公开访问(跳过人工审核)、可生成 QFLink 授权码
③ 生成「QFLink 授权码」
路径:个人设置 → QFLink授权码 标签页
- 在「用途说明」里随便写一句,例如
WorkBuddy自动化 - 点右边的蓝色按钮「生成 QFLink 授权码」
- 复制生成出来的那串码(格式
qf+ 32 位十六进制)
> 图挂了就看文字:个人中心 →「QFLink授权码」区域 → 填写用途说明 → 点蓝色按钮「生成 QFLink 授权码」→ 复制那串 qf 开头的码。
> 页面上写的「使用条件:仅已认证教师可用。未认证时即使用用户名+密码或 QFLink 授权码也会返回 403」,说的就是第 ② 步。
> 授权码可以随时在列表里点「停用」,停用后旧码立即失效——所以它比直接给密码安全。
④ 把这两样交给 AI
> 「我的 quickform 用户名是 xxx,QFLink 授权码是 qfxxxxxxxx...」
然后直接说:「把这个网页发到 quickform」。剩下的建任务、改代码、上传、给链接,AI 全包。
这是什么 / 不是什么
quickform.cn 是教育场景的数据收集服务(温州科技高中 + 温州大学出品)。核心模型:
- 每个任务有一个
apiid - 前端把数据
POST https://quickform.cn/api/<apiid>→ 进后台 - 后台可看作答记录、导出表格、做统计
它跟「把 HTML 传上去托管」是一体的:/cli/upload 上传后返回的公网地址,与 /api/<apiid> 同源,所以前端直接 fetch 不会有跨域问题。
⚠️ 容易搞错的地方:
- 不要用 CloudBase / Vercel / GitHub Pages 去实现「发到 quickform」——那是另一条路。
- 用户说「发到 quickform」= 走本文档。
- /cli/* 接口不是浏览器操作,不需要登录网页、不需要 Cookie,纯 HTTP。
凭据怎么处理
认证靠 username + password 或 auth_code(二选一,等价),放在请求体里。
| 方式 | 说明 | 建议 |
|---|---|---|
auth_code |
个人中心「QFLink 授权码」,qf+32hex,可随时停用 |
✅ 首选 |
password |
账号密码 | 仅当用户不愿意生成授权码时 |
硬规则:
- 先问用户要用户名 + 授权码(或密码),不要猜、不要复用别人的。
- 授权码/密码不要写进任何文件(包括本 Skill、脚本、仓库),只在当次命令里用。
- 先跑 getuser 确认 is_certified: true 再往下走;未认证就引导用户做第 0 步 ②。
- 认证失败多次会 429 限流,别反复试错密码。
环境坑(实测必踩)
- Bash / sandbox 里 curl 访问 https 会因 Windows schannel 吊销检查失败,报
CRYPT_E_REVOCATION_OFFLINE,http_code=000。
→ 所有 curl 必须加--ssl-no-revoke。 agent-browser是独立无头实例,不共享用户自己 Chrome 的登录态(默认--headless=new,看不到窗口)。别指望用它去 quickform 控制台点按钮。- 上传时文件名用 ASCII:中文文件名在 multipart 里易被编码搞乱。先
cp成_upload_xxx.html,传完删掉。 - 服务端是 http→https 301,脚本里统一用 https。
接口速查
认证字段(username + auth_code/password)全部放 body。
| 用途 | 请求 |
|---|---|
| 校验账号 | POST /cli/getuser → 看 is_certified |
| 任务列表 | POST /cli/list → tasks:[{apiid,name}] |
| 建任务 | POST /cli/add {task_name, task_intro} → {apiid} |
| 任务详情/数据 | POST /cli/show {apiid, include_data:true} → 含 task_detail_url、submissions |
| 上传 HTML | POST /cli/upload multipart:username/auth_code/apiid/file(仅 .html/.htm,≤4MB)→ {url, filename} |
| 提交数据 | POST /api/<apiid>(JSON 或 form-urlencoded) |
| 取全部数据 | GET /api/<apiid>/all |
# 建任务
curl -s --ssl-no-revoke -X POST "https://quickform.cn/cli/add" \
-H "Content-Type: application/json" \
-d '{"username":"<用户名>","auth_code":"<授权码>","task_name":"任务名","task_intro":"说明"}'
# 上传(先改成 ASCII 文件名)
cp "中文名.html" _upload_x.html
curl -s --ssl-no-revoke -X POST "https://quickform.cn/cli/upload" \
-F "username=<用户名>" -F "auth_code=<授权码>" \
-F "apiid=<apiid>" -F "file=@_upload_x.html;type=text/html"
# → {"url":"https://quickform.cn/uploads/<hash>.html?taskid=<apiid>"}
标准流程
- 要凭据:用户名 + 授权码(没认证就先引导做第 0 步)。
- 建任务:
/cli/add起个能看懂的task_name,task_intro写清用途。记下apiid。 - 改 HTML:注入提交逻辑(见下节)。
- 上传:ASCII 文件名 →
/cli/upload→ 拿到公网url。 - 验链路:
curl -sL拉一次页面确认返回的是自己的 HTML;再 POST 一条明确标注为自检的记录,/cli/show include_data:true确认字段落库。提醒用户:自检记录要在控制台手动删(没有删除接口)。 - 交付三件套:
- 🎒 学生入口:https://quickform.cn/uploads/<hash>.html?taskid=<apiid>
- 📊 后台数据页:task_detail_url(形如https://quickform.cn/task/<id>)
- 🔌 原始数据 API:https://quickform.cn/api/<apiid>/all
前端提交代码模板
关键点:字段名用中文(后台表头直接可读);同源 fetch 为主,sendBeacon 兜底(本地双击打开是跨域,fetch 会挂,靠 beacon 火-and-forget 送出)。
const QF_URL = `https://quickform.cn/api/<apiid>`;
let submitted = false;
function submitResult(payload, onOk){
if (submitted) return;
submitted = true;
let settled = false;
const timer = setTimeout(()=>{ if(!settled){ settled = true; beacon(payload, onOk); } }, 6000);
fetch(QF_URL, {
method: `POST`,
headers: { "Content-Type": `application/json` },
body: JSON.stringify(payload)
}).then(r=>r.text().then(t=>{
if (settled) return; settled = true; clearTimeout(timer);
let j = null; try { j = JSON.parse(t); } catch(e){}
if (j && j.status === `success`) onOk();
else { submitted = false; /* 显示「重新提交」按钮 */ }
})).catch(()=>{ if(settled) return; settled = true; clearTimeout(timer); beacon(payload, onOk); });
}
function beacon(payload, onOk){
if (!navigator.sendBeacon) return;
const fd = new URLSearchParams();
Object.keys(payload).forEach(k => fd.append(k, payload[k]));
// 必须用 safelisted 类型,否则会触发预检而失败
const blob = new Blob([fd.toString()], { type: `application/x-www-form-urlencoded` });
if (navigator.sendBeacon(QF_URL, blob)) onOk();
}
字段一致性:后台表头由第一条提交推断,所以每次提交的 key 必须完全一致(缺字段会留空)。改字段名 = 开新任务。
注意事项
- 上传后
is_public_approved可能为false,但只要账号已认证,uploads链接能直接打开(返回原 HTML,不是审核页)。 - 没有公开的「删除提交记录」接口,误提交的记录只能用户在控制台手删。
- 官方还有 PyPI 包
quickform-cli(命令qf),同一个后端;接口文档见 https://quickform.cn/cli 。 - 打包分发给别人时:整个
quickform-publish/文件夹一起给(含assets/截图),对方放到自己的~/.workbuddy/skills/下即可。