API 文档
本文档面向对接小程序、移动 App 等第三方客户端的开发者,描述 BlogKit 开放 API 的使用方式。适用 API 版本:v1。
1. 概述
BlogKit 的开放 API 由「开放API」插件提供,统一入口为站点根目录下的 api.php。主系统本身不实现任何 API 逻辑;未安装或未启用该插件时,api.php 返回:
{"code": 404, "message": "API 功能未启用:请安装并启用 openapi(开放API套件)插件"}能力范围:文章、评论、分类、标签、页面、用户、搜索、通知的查询与写入,登录注册等认证接口,图片/文件/头像上传,系统信息查询。
基础地址:{站点域名}/api.php,路径中带版本段,如 /api.php/v1/articles。
版本机制:路径前缀 /api.php/v1/...(推荐);或请求头 Accept-Version: v1;两者均未提供时使用后台配置的默认版本(默认 v1)。每个响应附带 X-API-Version 响应头;请求未启用的版本时返回 404:{"code": 404, "message": "API版本无效", "data": {"requested_version": "v2", "supported_versions": ["v1"]}}。
2. 快速开始
- 后台进入「插件管理」,安装并启用「开放API」插件(openapi)。
- 后台进入「插件管理 → 插件详情 → 配置」:确认「启用API功能」已开启;在密钥管理中创建 API 密钥(填写名称、平台,可选配置 IP 白名单、域名白名单、限流与过期时间)。密钥明文仅在创建时展示一次,请立即保存。
- 验证安装,请求测试接口:
curl http://example.com/api.php/v1/test{"code": 200, "message": "测试成功", "data": {"message": "API路由测试成功", "request_path": {}, "request_method": "GET"}}- 获取文章列表(公开接口,无需认证):
curl "http://example.com/api.php/v1/articles?page=1&page_size=5"。
3. 认证方式
3.1 认证模式
后台「API 设置」中的「认证方式」(api_auth_type)决定鉴权模式:
session(默认):基于登录会话。调用POST /auth/login成功后服务端建立会话,客户端后续携带会话 Cookie 访问需登录接口;登录响应中的token字段为服务端生成的令牌(有效期由「Token过期时间」配置控制,默认 3600 秒),可通过/auth/refresh-token刷新。api_key:基于 API 密钥,密钥在每次请求中传递(见 3.2)。
两种模式共存:带会话 Cookie 的调用走会话认证,带密钥的调用走密钥认证。通过 API Key 认证的请求在接口层视为管理员身份(可调用分类、标签、页面的管理接口)。
3.2 API 密钥的传递
| 方式 | 示例 | 说明 |
|---|---|---|
| 请求头 | API-Key: <你的密钥> | 推荐 |
| 查询参数 | ?api_key=<你的密钥> | 通用,适合无法自定义请求头的场景 |
说明:入口分发层额外识别 X-API-Key 请求头,但部分接口的控制器在会话缺失时仅从 API-Key 请求头或 api_key 参数读取密钥,因此第三方客户端请统一使用上表两种方式。
3.3 密钥校验规则
每次使用密钥的请求,服务端依次校验:密钥有效性(bcrypt 加密存储、指纹定位后校验)→ 状态(已撤销 inactive 或已过期 expired 拒绝访问)→ 过期时间 → IP 白名单(逗号分隔,比对客户端 IP)→ 域名白名单(逗号分隔,比对请求 Referer)→ 接口权限(若密钥配置了 permissions,即允许访问的接口路径 JSON 数组,请求路径必须命中列表)→ 密钥级限流(按密钥设置的次数与时间窗口计数)。
3.4 认证错误示例
未提供或无效密钥(访问需认证接口时):{"code": 401, "message": "认证失败,请检查认证信息"}。
密钥存在但校验不通过时返回 403,message 取以下值之一:无效的API Key、API Key已被撤销、API Key已过期、IP地址不在白名单中、域名不在白名单中、API Key没有访问该资源的权限、API Key请求频率超过限制。例如:{"code": 403, "message": "IP地址不在白名单中"}。
3.5 会话模式下的 CSRF 校验
session 模式下,写请求(POST/PUT/DELETE)默认须通过 CSRF 校验:在请求体中携带 csrf_token 字段,或请求头 X-CSRF-Token(也接受 X-XSRF-Token、X-CSRFToken)。携带 API 密钥的请求豁免 CSRF 校验。后台关闭「CSRF 防护」后全部放行。
3.6 接口鉴权级别
- 公开接口:各列表/详情查询、搜索、系统信息、登录注册等;
- 需登录:文章/评论的写操作、点赞/收藏/关注、我的收藏/点赞/历史、令牌刷新等(session 模式要求会话用户;api_key 模式要求有效密钥);
- 需登录会话:上传接口、通知接口基于登录会话鉴权,不支持纯密钥调用;
- 需管理员:分类/标签/页面的写操作、删除用户(session 模式要求管理员会话;api_key 模式视为管理员)。
4. 通用约定
4.1 响应格式
所有接口返回统一包裹结构:code 为业务状态码(与 HTTP 语义一致),message 为提示文本,data 为数据负载(失败时通常为 null)。框架层错误(限流、接口不存在等)的响应额外携带 timestamp 字段(Unix 时间戳)。
{"code": 200, "message": "获取成功", "data": {}}4.2 分页
列表接口通用请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | int | 否 | 页码,默认 1 |
| page_size | int | 否 | 每页数量,默认取后台配置(默认 20),上限取后台配置(默认 100),小于 1 时按 1 处理 |
分页响应的 data 结构(下文「分页响应」均指此结构):
{
"items": [],
"pagination": {"total": 42, "page": 1, "page_size": 20, "total_pages": 3, "has_next": true, "has_prev": false}
}4.3 时间格式
created_at、updated_at 等时间字段为 Unix 时间戳(秒);文章详情额外返回 created_at_formatted(格式 Y-m-d H:i:s);/system/info 的 server_time 为 Y-m-d H:i:s 格式文本。
4.4 错误码表
| code | 说明 | 常见场景 |
|---|---|---|
| 200 | 成功 | — |
| 400 | 请求参数错误 | 缺少必填参数、字段校验失败、ID 无效 |
| 401 | 未认证 | 未登录、密钥缺失或无效、用户名或密码错误 |
| 403 | 禁止访问 | 权限不足、API 功能被禁用、依赖插件未启用、CSRF 校验失败、白名单或权限拦截 |
| 404 | 资源不存在 | 数据不存在、接口路径或 API 版本无效 |
| 429 | 请求过于频繁 | 触发全局限流 |
| 500 | 服务器内部错误 | 服务端异常,生产环境返回通用提示「服务器繁忙,请稍后重试」 |
字段校验失败(400)时响应附带 errors 对象:{"code": 400, "message": "参数验证失败", "errors": {"title": "title不能为空"}}。API 功能总开关关闭时,所有请求返回 {"code": 403, "message": "API功能已禁用"}。
4.5 插件依赖
通知接口(/notifications*)与文章点赞/收藏写入接口依赖「消息通知」插件;文章与评论的点赞/取消点赞、收藏/取消收藏、关注关系、我的收藏/点赞/阅读历史接口依赖「互动增强」插件。对应插件未启用时返回 403,message 形如「互动功能未启用:请安装并启用 interaction(互动增强)插件」。
5. 接口列表
以下路径省略基础地址与版本前缀,实际请求如 {站点域名}/api.php/v1/articles。请求体支持表单或 JSON(application/json);PUT/DELETE 仅支持 JSON 请求体。
5.1 系统接口
GET /system/config — 获取公开站点配置
无参数。data 为公开配置项:site_name、site_url、site_description、site_keywords、pagination_count、comment_enabled、like_enabled、favorite_enabled、follow_enabled、read_history_enabled、message_enabled、message_duration、register_enabled、login_enabled、api_enabled、rewrite_enabled。
GET /system/info — 获取系统信息
{"code": 200, "message": "获取成功", "data": {"version": "1.0.0", "name": "BlogKit", "description": "一个轻量博客站点", "php_version": "8.2.0", "server_time": "2026-10-09 12:00:00"}}GET /system/stats — 获取内容统计
{"code": 200, "message": "获取成功", "data": {"articles": 12, "users": 3, "comments": 45, "categories": 5, "tags": 18}}GET /test — 路由测试
无参数,响应示例见「快速开始」。data.request_path 为本次请求的查询参数。
5.2 认证接口
POST /auth/login — 用户登录
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| username | string | 是 | 用户名 |
| password | string | 是 | 密码 |
curl -X POST "http://example.com/api.php/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"username": "demo", "password": "123456"}'{"code": 200, "message": "登录成功", "data": {"user_id": 1, "username": "demo", "nickname": "演示用户", "email": "demo@example.com", "token": "a1b2c3d4e5f6g7h8", "token_expire": 3600}}可能错误:401 用户名或密码错误;403 用户已被禁用。session 模式下登录成功即建立会话,需登录接口须携带会话 Cookie。
POST /auth/register — 用户注册
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| username | string | 是 | 用户名,3-50 个字符 |
| password | string | 是 | 密码,6-50 个字符 |
| string | 是 | 合法邮箱 | |
| nickname | string | 否 | 昵称,缺省取用户名 |
成功:{"code": 200, "message": "注册成功", "data": {"user_id": 5}}。可能错误:400 用户名已存在、邮箱已被注册、参数校验失败。
POST /auth/logout — 退出登录(需登录)
无参数。成功:{"code": 200, "message": "退出成功", "data": null}。
POST /auth/forgot-password — 忘记密码
参数:email(string,必填,注册邮箱)。成功:data 为 null,message 为「重置邮件已发送」,服务端生成有效期 24 小时的重置令牌。可能错误:400 邮箱未注册。
POST /auth/reset-password — 重置密码
参数:email(string,必填,注册邮箱)、token(string,必填,重置令牌)、password(string,必填,新密码,6-50 个字符)。成功:{"code": 200, "message": "密码重置成功", "data": null}。可能错误:400 邮箱未注册、重置令牌无效、重置令牌已过期。
POST /auth/refresh-token — 刷新令牌(需登录)
{"code": 200, "message": "Token刷新成功", "data": {"user_id": 1, "username": "demo", "token": "a1b2c3d4e5f6g7h8", "token_expire": 3600}}5.3 文章接口
GET /articles — 文章列表
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | int | 否 | 页码 |
| page_size | int | 否 | 每页数量 |
| category_id | int | 否 | 按分类筛选 |
| tag_id | int | 否 | 按标签筛选 |
| status | int | 否 | 状态:1-已发布(默认)、0-草稿 |
| search | string | 否 | 关键词过滤 |
data.items 元素为文章表全量字段及关联字段,节选如下:
{
"id": 1,
"title": "第一篇文章",
"content": "正文内容……",
"excerpt": "摘要……",
"cover_image": "/uploads/images/2026/10/09/image_xxx.png",
"category_id": 1,
"user_id": 1,
"status": 1,
"view_count": 128,
"like_count": 6,
"comment_count": 3,
"top_type": 0,
"slug": "hello-blogkit",
"created_at": 1760000000,
"updated_at": 1760000000,
"category_name": "默认分类",
"category_slug": "default",
"author": "演示用户",
"username": "demo",
"favorite_count": 2
}GET /articles/{id} — 文章详情
路径参数 id。字段同列表元素,额外返回 created_at_formatted 与 views;访问详情会使文章阅读量加一。可能错误:400 文章ID无效;404 文章不存在。
GET /articles/top — 置顶文章列表
参数:top_types(string,可选,置顶类型逗号分隔,默认 1,2,3,即 1-全局置顶、2-首页置顶、3-分类置顶)、category_id(int,可选,分类置顶时使用)、limit(int,可选,数量上限,默认 10)。data 为文章数组,不分页。
GET /articles/search — 搜索文章
参数:keyword(string,必填,搜索关键词)、page/page_size(int,可选,分页参数)。分页响应。可能错误:400 搜索关键词不能为空。
POST /articles — 创建文章(需登录)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| title | string | 是 | 标题,最长 60 字符 |
| content | string | 是 | 正文内容 |
| category_id | int | 是 | 分类 ID |
| tags | string | 否 | 标签,逗号分隔 |
| status | int | 否 | 0-草稿(默认)、1-发布 |
| excerpt | string | 否 | 摘要,最长 200 字符 |
成功:{"code": 200, "message": "文章创建成功", "data": {"article_id": 9}}。可能错误:400 参数校验失败或长度超限;401 未登录;403 新注册账号处于发帖限制期。
PUT /articles/{id} — 更新文章(需登录,仅作者)
路径参数 id。请求体字段均可选:title、content、category_id、tags、status、excerpt,仅更新传入的字段。成功:{"code": 200, "message": "文章更新成功", "data": null}。可能错误:400 文章ID无效;401 未登录;403 无权修改此文章;404 文章不存在。
DELETE /articles/{id} — 删除文章(需登录,仅作者)
成功:{"code": 200, "message": "文章删除成功", "data": null}。可能错误:400 文章ID无效;401 未登录;403 无权删除此文章;404 文章不存在。
POST /articles/{id}/like 与 POST /articles/{id}/unlike — 点赞 / 取消点赞文章(需登录)
成功分别返回 {"code": 200, "message": "点赞成功", "data": {"like_count": 7}} 与 {"code": 200, "message": "取消点赞成功", "data": {"like_count": 6}}。依赖「互动增强」「消息通知」插件。
POST /articles/{id}/favorite 与 POST /articles/{id}/unfavorite — 收藏 / 取消收藏文章(需登录)
成功分别返回 {"code": 200, "message": "收藏成功", "data": {"favorite_count": 3}} 与 {"code": 200, "message": "取消收藏成功", "data": {"favorite_count": 2}}。依赖「互动增强」「消息通知」插件。
POST /articles/{id}/top — 设置文章置顶(需登录,作者或管理员)
参数:top_type(int,必填,0-取消置顶、1-全局置顶、2-首页置顶、3-分类置顶)。成功:{"code": 200, "message": "置顶设置成功", "data": {"top_type": 1}}。可能错误:400 文章ID无效或置顶类型无效;403 无权设置此文章置顶;404 文章不存在。
5.4 评论接口
GET /articles/{article_id}/comments — 文章评论列表
通用分页参数(page、page_size)。评论按 parent_id 组织为两级树结构,每条评论的 replies 数组存放子评论。元素字段节选:
{
"id": 10,
"article_id": 1,
"parent_id": 0,
"user_id": 2,
"content": "写得不错",
"status": 1,
"is_top": 0,
"created_at": 1760001000,
"username": "zhangsan",
"nickname": "张三",
"avatar": "/uploads/avatars/avatar_2_xxx.png",
"user_liked": 0,
"is_author": 0,
"replies": []
}可能错误:400 文章ID无效;404 文章不存在。
POST /articles/{article_id}/comments — 发表评论(需登录)
参数:article_id(int,必填,文章 ID,服务端从请求体读取)、content(string,必填,评论内容)。成功:{"code": 200, "message": "评论发表成功", "data": {"comment_id": 11}}。注意:article_id 须通过请求体传入,路径中的 article_id 不参与解析,请保证两者一致。可能错误:400 文章ID无效或评论内容为空;401 未登录;404 文章不存在。
PUT /comments/{id} — 更新评论(需登录,仅作者)
参数:content(string,必填,新的评论内容)。成功:{"code": 200, "message": "评论更新成功", "data": null}。可能错误:400 评论ID无效或内容为空;403 无权修改此评论;404 评论不存在。
DELETE /comments/{id} — 删除评论(需登录,仅作者)
成功:{"code": 200, "message": "评论删除成功", "data": null}。可能错误同上。
POST /comments/{id}/like 与 POST /comments/{id}/unlike — 点赞 / 取消点赞评论(需登录)
成功分别返回 {"code": 200, "message": "点赞成功", "data": {"like_count": 2}} 与 {"code": 200, "message": "取消点赞成功", "data": {"like_count": 1}}。依赖「互动增强」插件。
5.5 分类接口
GET /categories — 分类列表
通用分页参数。元素字段:id、name、slug、description、keywords、parent_id、sort、is_menu、created_at。
GET /categories/{id} — 分类详情
可能错误:400 分类ID无效;404 分类不存在。
POST /categories — 创建分类(需管理员)
参数:name(string,必填,分类名称,最长 100 字符)、slug(string,可选,分类别名,缺省取名称)、description(string,可选,描述)、parent_id(int,可选,父分类 ID,默认 0)。成功:{"code": 200, "message": "分类创建成功", "data": {"category_id": 3}}。可能错误:400 参数校验失败;401 未登录;403 无权创建分类。
PUT /categories/{id} — 更新分类(需管理员)
字段均可选:name、slug、description、parent_id。成功:{"code": 200, "message": "分类更新成功", "data": null}。可能错误:403 无权修改分类;404 分类不存在。
DELETE /categories/{id} — 删除分类(需管理员)
成功:{"code": 200, "message": "分类删除成功", "data": null}。可能错误:403 无权删除分类;404 分类不存在。
5.6 标签接口
GET /tags — 标签列表
通用分页参数。元素字段:id、name、count(使用次数)、created_at。
GET /tags/{id} — 标签详情
可能错误:400 标签ID无效;404 标签不存在。
POST /tags — 创建标签(需管理员)
参数:name(string,必填,标签名称,最长 100 字符)。成功:{"code": 200, "message": "标签创建成功", "data": {"tag_id": 4}}。可能错误:400 参数校验失败;401 未登录;403 无权创建标签。
PUT /tags/{id} — 更新标签(需管理员)
可选字段:name。成功:{"code": 200, "message": "标签更新成功", "data": null}。可能错误:403 无权修改标签;404 标签不存在。
DELETE /tags/{id} — 删除标签(需管理员)
成功:{"code": 200, "message": "标签删除成功", "data": null}。可能错误:403 无权删除标签;404 标签不存在。
5.7 页面接口
GET /pages — 页面列表
通用分页参数。元素字段:id、title、content、description、keywords、slug、status、is_menu、created_at、updated_at。
GET /pages/{id} — 页面详情
可能错误:400 页面ID无效;404 页面不存在。
5.8 用户接口
GET /users — 用户列表
通用分页参数。元素为用户表行,节选字段:id、username、nickname、email、avatar、bio、website、role、status、created_at。
GET /users/{id} — 用户详情
字段同上。可能错误:400 用户ID无效;404 用户不存在。
POST /users — 注册用户
参数与 POST /auth/register 一致(username、password、email 必填,nickname 可选)。成功:{"code": 200, "message": "注册成功", "data": {"user_id": 6}}。可能错误:400 参数校验失败。
PUT /users/{id} — 更新用户(需登录,仅本人)
参数:id(int,必填,用户 ID,服务端从请求体读取,路径中的 id 不参与解析)、email(string,可选,新邮箱)、nickname(string,可选,新昵称)。成功:{"code": 200, "message": "更新成功", "data": null}。可能错误:400 用户ID无效;401 未登录;403 无权修改此用户。
DELETE /users/{id} — 删除用户(需登录,管理员)
id 同样从请求体读取。成功:{"code": 200, "message": "删除成功", "data": null}。可能错误:400 用户ID无效或不能删除自己;403 无权删除用户。
POST /users/{id}/follow — 关注用户(需登录)
成功:{"code": 200, "message": "关注成功", "data": null}。可能错误:400 用户ID无效或不能关注自己;401 未登录。依赖「互动增强」插件。
POST /users/{id}/unfollow — 取消关注(需登录)
成功:{"code": 200, "message": "取消关注成功", "data": null}。
GET /users/{id}/followers 与 GET /users/{id}/following — 粉丝列表 / 关注列表(需登录)
通用分页参数,分页响应。
GET /users/me/favorites、GET /users/me/likes、GET /users/me/history — 我的收藏 / 我的点赞 / 阅读历史(需登录)
通用分页参数,分页响应。依赖「互动增强」插件。
5.9 搜索接口
GET /search — 全局搜索
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| keyword | string | 是 | 搜索关键词 |
| type | string | 否 | 搜索类型:article(默认)、user、comment |
| page | int | 否 | 页码 |
| page_size | int | 否 | 每页数量 |
分页响应,items 内容随 type 变化。可能错误:400 搜索关键词不能为空。
5.10 上传接口(需登录会话)
均为 multipart/form-data 请求。
POST /upload/image — 上传图片
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| image | file | 是 | 图片文件,支持 JPG/PNG/GIF/WEBP,不超过 5MB |
{"code": 200, "message": "上传成功", "data": {"url": "/uploads/images/2026/10/09/image_1760000000_1234.png", "filename": "image_1760000000_1234.png", "size": 102400}}可能错误:400 图片上传失败、格式不支持、大小超限。
POST /upload/file — 上传文件
参数:file(file,必填,支持 PDF/Word/Excel/ZIP/RAR,不超过 20MB)。响应 data 同图片接口(url、filename、size),url 形如 /uploads/files/2026/10/09/file_xxx.pdf。
POST /upload/avatar — 上传头像(需登录会话)
参数:avatar(file,必填,图片文件,支持 JPG/PNG/GIF/WEBP,不超过 2MB)。成功后同时更新当前用户头像。
{"code": 200, "message": "头像上传成功", "data": {"url": "/uploads/avatars/avatar_1_1760000000.png", "filename": "avatar_1_1760000000.png"}}5.11 通知接口(需登录会话)
依赖「消息通知」插件。
GET /notifications — 通知列表
通用分页参数,分页响应。
GET /notifications/unread — 未读数量
{"code": 200, "message": "获取成功", "data": {"unread_count": 3}}PUT /notifications/{id}/read — 标记通知已读
路径参数 id。成功:{"code": 200, "message": "标记成功", "data": null}。可能错误:400 通知ID无效;403 无权操作此通知;404 通知不存在。
PUT /notifications/read-all — 全部标记已读
无参数。成功:{"code": 200, "message": "全部标记成功", "data": null}。
6. 钩子扩展
开放 API 不提供对外推送(Webhook)。API 处理生命周期中会触发系统内部钩子,其他插件可监听这些钩子介入流程:
| 钩子 | 触发时机 | 携带数据 |
|---|---|---|
| api_request_before | 请求分发前 | path、method、params |
| api_response_before | 控制器返回后、输出前 | path、method、result |
| api_response_after | 响应输出后 | path、method |
| api_error | 控制器抛出异常时 | path、method、error、trace |
插件的钩子注册方式见 插件开发指南。
7. 常见问题
跨域(CORS)
后台「API 设置」中开启「启用CORS跨域支持」后,入口层输出 Access-Control-Allow-Origin(来源由「允许的跨域来源」控制,默认 *)等响应头,并对 OPTIONS 预检请求直接返回。未开启时,浏览器跨域调用会被拦截;小程序、原生 App 的服务端请求不受 CORS 限制。
密钥泄露处理
发现密钥泄露时,立即在后台该插件的密钥管理中「撤销」对应密钥(状态置为 inactive,之后请求返回 401「API Key已被撤销」),排查影响范围后新建密钥。建议为每个客户端应用分配独立密钥,并配置 IP 白名单与过期时间;密钥明文仅在创建时展示一次,无法再次查看。
限流
开放 API 提供两级限流:全局限流在后台「API 设置」中开启「启用请求频率限制」,按客户端 IP 以固定时间窗口计数(次数与窗口大小均可配置),触发后返回 429「请求过于频繁,请稍后重试」;密钥级限流按创建或编辑密钥时设置的次数、时间窗口与单位(秒/分/时/天)计数,超出后该密钥的校验失败,返回 403「API Key请求频率超过限制」。