用 REST API 让 AI Agent 帮你写博客:weiserv 博客自动化实践
背景:让内容生产可以"被调用"
个人技术站要持续产出内容,一个痛点是"写作→发布"的链路太手工。既然站点是 Django 写的,顺手用 Django REST Framework(DRF)做了一套博客管理 API,把"发文章"变成一次 HTTP 请求。这样 AI agent、定时任务、或其他系统都能通过标准接口管理博客,而不必登录后台手动粘贴 Markdown。
本文不堆砌 DRF 入门,而是聚焦接口契约设计和安全边界取舍——这两点才是实战里容易翻车的地方。
接口概览
API 挂在 /api/v1/ 下,博客相关两个核心端点:
| 方法 | 路径 | 说明 | 鉴权 |
|---|---|---|---|
| GET | /api/v1/posts/ |
列表(分页,匿名可见已发布) | 公开 |
| POST | /api/v1/posts/ |
创建文章 | Token |
| GET | /api/v1/posts/<slug>/ |
详情 | 公开 |
| PUT/PATCH | /api/v1/posts/<slug>/ |
全量/局部更新 | Token |
| DELETE | /api/v1/posts/<slug>/ |
删除 | Token |
| POST | /api/v1/upload/ |
图片上传(multipart,≤5MB) | Token |
列表默认按时间倒序,每页 20 条,响应形如 {"count":..., "next":..., "previous":..., "results":[...]}。
鉴权:Token 怎么来、怎么带
接口用 DRF 的 TokenAuthentication。请求头这样带:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
注意是 Token 不是 Bearer。关键点:项目没有 token 签发端点(没注册 obtain_auth_token),Token 必须在服务器端用 manage.py shell 生成一次,妥善保存:
python manage.py shell -c \
"from django.contrib.auth.models import User; \
from rest_framework.authtoken.models import Token; \
u=User.objects.get(username='admin'); \
t,_=Token.objects.get_or_create(user=u); print(t.key)"
创建文章的字段约定(易错点)
curl -X POST "https://weiserv.com/api/v1/posts/" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "通过 REST API 发博",
"slug": "rest-api-test-post",
"excerpt": "用 DRF 接口发布的示例",
"body": "## 正文\n\n这是 Markdown 正文。",
"category": 1,
"tags": ["Django", "API"],
"published": true
}'
三个容易踩的约定:
category传整数 ID,不是分类名。分类是core.Category表的外键,API 接收主键数字;传不存在的 ID 会返回 400{"category":["Invalid pk ..."]}。slug唯一。重复创建直接 400{"slug":["slug already in use"]}——API 不会像"草稿箱"那样静默跳过,调用方需自己保证 slug 不冲突(或用 PATCH 改已有文章)。published是布尔:true即上线,false即草稿。项目没有status枚举字段,就是这一个布尔开关。tags传字符串数组,服务器端用taggit的set()关联。
响应里 tags 以字符串数组返回,category_name 一并给出,方便调用方核对。
两条发博路径的安全边界
个人站其实有两条发博通道,区别在于鉴权模型和安全边界:
| 维度 | REST API(本文) | 服务器端直发 |
|---|---|---|
| 通道 | HTTPS 调公网 /api/v1/posts/ |
SSH 到服务器跑 Django 脚本 |
| 鉴权 | Token,请求头携带 | 不需要 Token,靠 SSH 密钥 |
| Token 是否进对话 | 会(有泄露风险) | 不出对话(最安全) |
| 分类传参 | 整数 ID | 分类名,get_or_create 自动建 |
| 幂等语义 | slug 重复直接 400 | 已存在则跳过(SKIP) |
对个人站来说,"Token 不进对话"是一条硬安全约定——所以我自己平时发博走的是服务器端直发(Token 全程不出现在任何对话里)。REST API 这条路更适合外部系统 / AI agent 通过标准 HTTP 集成的场景:调用方愿意自己保管 Token,且通常通过服务端程序(curl、后端任务)调用,而非浏览器。
补充一个 CORS(跨域资源共享)现状:当前只对
/api/路径启用 CORS 中间件,但没配跨域白名单。这意味着浏览器里从别的域名网页调这个 API 会被拦;但 curl、服务端到服务端、AI agent 这类不带Origin头的非浏览器调用完全不受影响。若将来要支持浏览器端第三方页面调用,才需要在settings.py补CORS_ALLOWED_ORIGINS。
小结
把"发博客"抽象成一个有清晰契约的 REST 接口,价值不在炫技,而在于让内容生产可被程序调度:agent 写完稿直接 POST 上线、定时任务批量维护、外部系统同步内容,都成了几行代码的事。设计时的两个核心取舍——Token 的保管边界与字段契约(ID vs 名称、slug 唯一性、布尔 published)——决定了这套接口是"好用且安全"还是"能用但埋雷"。把这两点想清楚,自动化才真正可靠。