本文档描述当前后端代码实际提供的 HTTP 接口。
- 基础地址:
http://localhost:8080 - 数据格式:除非另有说明,否则使用 JSON
- 认证方式:Spring Security 会话 Cookie(
JSESSIONID) - 默认验证码长度:
6 - 默认上传限制:
10 MB - 英文版本:API.md
登录成功后,服务器通过 JSESSIONID Cookie 维护登录状态。访问 /u/** 需要用户具有 USER 或 ADMIN 权限;访问 /back/** 需要 ADMIN 权限。/a/** 和 /p/** 允许匿名访问。
同一账户最多允许一个活动会话。
所有修改状态的现有接口都启用了 CSRF 防护,包括 /a/**、/p/** 下允许匿名访问的 POST 接口。服务端渲染的页面通过以下内容提供令牌:
<meta name="csrf-token" content="...">
<meta name="csrf-header" content="X-CSRF-TOKEN">JSON 和 multipart 请求应使用页面提供的请求头名称和令牌。HTML 表单应提交隐藏字段 _csrf。
X-CSRF-TOKEN: <token>CSRF 校验失败由 Spring Security 直接处理,不经过业务异常处理器。
业务接口成功时返回 200 OK。JSON 接口使用 { "data": ... } 包装;敏感资料修改返回 302 Location: /login。
错误响应同样使用 JSON 包装。参数校验和账户流程异常返回 400 Bad Request;内容操作越权返回 403 Forbidden;文章、评论或公开用户不存在时返回 404 Not Found;同一作者的文章标题冲突返回 409 Conflict;未预期异常返回 500 Internal Server Error。认证和 CSRF 失败可能由 Spring Security 直接处理。
资料修改、内容校验、后台约束以及内容域的 403、404、409 会返回具体业务消息。其他账户流程统一返回 {"data":"Request error"},避免泄露敏感账户状态。上传超过限制时返回 {"data":"Max upload size exceeded"}。
登录表单认证失败由 Spring Security 直接处理,不经过统一异常处理器:服务端返回 302 Location: /login?error,登录页根据 error 查询参数显示统一的失败提示。未认证访问其他受保护接口时,Spring Security 也可能重定向到 /login,而不是返回 JSON 401。
| 方法 | 路径 | 认证 | CSRF | 描述 |
|---|---|---|---|---|
POST |
/a/register/verify |
公开 | 必需 | 发送注册验证码 |
POST |
/a/register |
公开 | 必需 | 注册账户 |
POST |
/a/reset-password/verify |
公开 | 必需 | 发送重置密码验证码 |
POST |
/a/reset-password |
公开 | 必需 | 重置密码 |
POST |
/a/login |
公开 | 必需 | 登录 |
POST |
/logout |
已认证 | 必需 | 退出登录 |
GET |
/u/me |
USER 或 ADMIN |
否 | 获取当前用户资料 |
POST |
/u/me/modify/name |
USER 或 ADMIN |
必需 | 修改用户名 |
POST |
/u/me/modify/description |
USER 或 ADMIN |
必需 | 修改简介 |
POST |
/u/me/modify/password |
USER 或 ADMIN |
必需 | 修改密码 |
POST |
/u/me/modify/email/verify |
USER 或 ADMIN |
必需 | 发送新邮箱验证码 |
POST |
/u/me/modify/email |
USER 或 ADMIN |
必需 | 修改邮箱 |
POST |
/u/me/modify/avatar |
USER 或 ADMIN |
必需 | 上传当前用户头像 |
GET |
/back/users |
ADMIN |
否 | 分页获取用户列表 |
POST |
/back/modify/user |
ADMIN |
必需 | 修改指定用户的完整管理资料 |
POST |
/back/create/user |
ADMIN |
必需 | 创建用户 |
POST |
/back/delete/user |
ADMIN |
必需 | 删除用户 |
DELETE |
/back/posts/{postId} |
ADMIN |
必需 | 删除任意文章及其讨论数据 |
DELETE |
/back/comments/{commentId} |
ADMIN |
必需 | 审核删除任意评论 |
GET |
/p/users/{id} |
公开 | 否 | 获取公开用户资料 |
GET |
/p/users/{id}/avatar |
公开 | 否 | 获取公开用户头像跳转 |
GET |
/avatars/{filename} |
公开 | 否 | 获取头像文件 |
GET |
/p/posts |
公开 | 否 | 按热点分页查询文章 |
GET |
/p/posts/hot |
公开 | 否 | 查询热点文章 |
GET |
/p/posts/search/author |
公开 | 否 | 按作者名模糊搜索文章 |
GET |
/p/posts/author/{authorId} |
公开 | 否 | 按作者 ID 查询文章 |
GET |
/p/posts/search/title |
公开 | 否 | 按标题模糊搜索文章 |
GET |
/p/posts/search/tag |
公开 | 否 | 按标签模糊搜索文章 |
GET |
/p/posts/{postId} |
公开 | 否 | 浏览文章并记录有效浏览量 |
POST |
/u/posts |
USER 或 ADMIN |
必需 | 创建文章 |
PUT |
/u/posts/{postId} |
USER 或 ADMIN |
必需 | 修改自己的文章 |
DELETE |
/u/posts/{postId} |
USER 或 ADMIN |
必需 | 删除自己的文章 |
GET |
/p/posts/{postId}/comments |
公开 | 否 | 查询文章根评论 |
GET |
/p/comments/{commentId}/replies |
公开 | 否 | 查询评论的直接回复 |
POST |
/u/posts/{postId}/comments |
USER 或 ADMIN |
必需 | 发布根评论 |
POST |
/u/comments/{commentId}/replies |
USER 或 ADMIN |
必需 | 回复评论 |
DELETE |
/u/comments/{commentId} |
USER 或 ADMIN |
必需 | 删除自己的评论 |
PUT |
/u/posts/{postId}/like |
USER 或 ADMIN |
必需 | 点赞文章 |
DELETE |
/u/posts/{postId}/like |
USER 或 ADMIN |
必需 | 取消文章点赞 |
PUT |
/u/comments/{commentId}/like |
USER 或 ADMIN |
必需 | 点赞评论 |
DELETE |
/u/comments/{commentId}/like |
USER 或 ADMIN |
必需 | 取消评论点赞 |
POST /a/register/verify
发送注册场景的邮件验证码。邮箱必须尚未注册。
Content-Type: application/json
X-CSRF-TOKEN: <token>{
"email": "user@example.com",
"scene": "REGISTER"
}字段要求:
email:必填并且必须匹配有效邮箱格式。scene:必须为REGISTER。
成功响应:200 OK,application/json
{
"data": null
}POST /a/register
使用注册验证码创建账户。注册成功后不会自动登录。
Content-Type: application/json
X-CSRF-TOKEN: <token>{
"username": "alice",
"password": "secret123",
"email": "user@example.com",
"verificationCode": "A1B2C3"
}字段要求:
username:必填、非空、不得包含普通空格、最多 32 个字符,并且必须唯一。password:必填、不得包含普通空格,长度为 6 至 32 个字符。email:必填、格式有效并且必须唯一。verificationCode:必填,长度必须符合验证码配置,默认为 6。
成功响应:200 OK,application/json
{
"data": {
"message": "Register successfully",
"name": "alice"
}
}POST /a/reset-password/verify
向已注册邮箱发送高风险操作验证码。
Content-Type: application/json
X-CSRF-TOKEN: <token>{
"email": "user@example.com",
"scene": "HIGH_RISK"
}字段要求:
email:必填、格式有效,并且必须属于现有账户。scene:必须为HIGH_RISK。
成功响应:200 OK,application/json
{
"data": null
}POST /a/reset-password
验证码和邮箱必须属于同一账户。成功后验证码失效,并通过会话注册表使匹配到的已有会话过期。
Content-Type: application/json
X-CSRF-TOKEN: <token>{
"email": "user@example.com",
"newPassword": "newSecret123",
"verificationCode": "D4E5F6"
}字段要求:
email:必填、格式有效,并且必须属于现有账户。newPassword:必填、不得包含普通空格,长度为 6 至 32 个字符。verificationCode:必填,长度必须符合验证码配置。
成功响应:200 OK,application/json
{
"data": "Password reset successfully"
}POST /a/login
该接口由 Spring Security 表单登录提供,不接收 JSON。
Content-Type: application/x-www-form-urlencodedusername=alice&password=secret123&_csrf=<token>
- 成功:设置会话 Cookie,并以
302重定向至/dashboard。 - 失败:以
302 Location: /login?error重定向回登录页。GET /login?error返回登录页面,并显示“用户名或密码错误,请重试”;失败响应不是 JSON。
POST /logout
需要有效会话和 CSRF 表单字段。
_csrf=<token>
成功后使当前会话失效,并默认以 302 重定向至 /login?logout。
GET /u/me
认证:需要 USER 或 ADMIN 权限。
成功响应:200 OK,application/json
{
"data": {
"userId": 1,
"userName": "alice",
"userEmail": "user@example.com",
"description": "Hello",
"createdAt": "2026-07-29T12:00:00",
"enabled": true
}
}createdAt 使用 Jackson 对 LocalDateTime 的默认 ISO-8601 JSON 表示。
POST /u/me/modify/name,请求体为 {"newName":"alice2"}。目标用户始终取自当前登录会话。成功后返回 302 Location: /login。
POST /u/me/modify/description,请求体为 {"newDescription":"简介"}。
POST /u/me/modify/password,请求体为 {"oldPassword":"old","newPassword":"newSecret"}。成功后返回 302 Location: /login。
先使用 {"email":"new@example.com","scene":"RESET_EMAIL"} 调用 /u/me/modify/email/verify,再向 /u/me/modify/email 提交 {"email":"new@example.com","password":"currentPassword","verificationCode":"ABC123"}。成功后返回 302 Location: /login。
POST /u/me/modify/avatar
认证:需要 USER 或 ADMIN 权限。
Content-Type: multipart/form-data; boundary=...
X-CSRF-TOKEN: <token>| 名称 | 类型 | 必填 | 描述 |
|---|---|---|---|
file |
文件 | 是 | 新头像文件 |
要求:
- 文件必须非空且 MIME 类型以
image/开头。 - 后端会解析实际图片内容,不只信任文件扩展名。
- 支持 PNG、JPEG 和 GIF。
- 默认单文件和请求总大小上限均为 10 MB。
- 账户必须处于启用状态。
成功响应:200 OK,响应体为 {"data":null}。
GET /back/users
认证:仅限 ADMIN。支持 Spring Data Pageable 查询参数:
| 参数 | 示例 | 描述 |
|---|---|---|
page |
0 |
从 0 开始的页码 |
size |
20 |
每页记录数,服务端限制为 1..100 |
sort |
id,asc |
排序字段与方向;可重复传递 |
示例:GET /back/users?page=0&size=20&sort=id,asc
成功响应的 data 是稳定的分页对象:
{
"data": {
"content": [
{
"id": 2,
"username": "alice",
"email": "alice@example.com",
"avatarId": 2,
"role": "USER",
"enabled": true,
"createdAt": "2026-07-29T12:00:00",
"updatedAt": "2026-07-30T12:00:00"
}
],
"page": 0,
"size": 20,
"totalPages": 1,
"totalElements": 1,
"first": true,
"last": true
}
}POST /back/modify/user
认证:仅限 ADMIN。需要 JSON 和 CSRF 请求头。每次保存必须携带全部五个字段,不能只发送发生变化的字段:
{
"id": 2,
"username": "alice",
"email": "alice@example.com",
"role": "USER",
"enabled": true
}字段要求:
id:必填,必须对应现有用户。username:必填、非空、不得包含@或普通空格,最多 32 个字符。email:必填、非空,必须匹配配置的邮箱正则。role:必填,只能为USER或ADMIN。enabled:必填布尔值。DTO 使用基本类型,遗漏时会被绑定为false,因此客户端不得省略。
系统始终要求至少存在一个处于启用状态的 ADMIN;违反该约束时事务回滚。修改成功后,目标用户在会话注册表中匹配到的所有会话会被标记为过期。若管理员修改自己的账户,当前页面会跳转到登录页。
成功响应:200 OK,data 为修改后的用户视图,字段与列表项一致。
POST /back/create/user
认证:仅限 ADMIN。需要 JSON 和 CSRF 请求头。请求必须携带全部五个字段:
{
"username": "alice",
"email": "alice@example.com",
"password": "secret1",
"role": "USER",
"enabled": true
}字段要求:
username:必填、非空、不得包含@或普通空格,最多 32 个字符。email:必填、非空,必须匹配配置的邮箱正则。password:必填、非空、不得包含普通空格,长度为 6 至 64 个字符。role:必填,只能为USER或ADMIN。enabled:必须显式为true;当前后端不允许直接创建停用用户。
密码使用 BCrypt 编码后存储。成功响应为 200 OK,data 是新用户的用户视图,不返回密码或密码哈希。
POST /back/delete/user
认证:仅限 ADMIN。需要 JSON 和 CSRF 请求头。请求使用待删除用户最后一次从服务器取得的完整快照:
{
"id": 2,
"username": "alice",
"email": "alice@example.com",
"role": "USER",
"enabled": true
}id、username、email 和 role 必须与数据库中的用户一致,否则请求失败。后台页面也会携带 enabled。删除成功后目标用户的匹配会话会被标记为过期,响应为 {"data":true}。服务会检查系统中是否仍有启用的管理员。
DELETE /back/posts/{postId}
认证:仅限 ADMIN。必须携带 CSRF 令牌,请求无正文。管理员可以删除任意作者的文章。整个操作在一个事务中删除文章下的评论、评论点赞、文章点赞、标签记录和浏览去重缓存,随后文章不可再访问。
成功返回 200 OK;文章不存在时返回 404 Not Found。
DELETE /back/comments/{commentId}
认证:仅限 ADMIN。必须携带 CSRF 令牌,请求无正文。管理员可以删除任意发布者的评论。叶子评论执行物理删除;存在回复的评论转换为与用户自删相同的删除占位节点,保证回复仍可访问。点赞和活跃评论计数在同一事务中更新。对仍存在的软删除占位节点重复请求为幂等成功。
成功返回 200 OK;评论不存在时返回 404 Not Found。
GET /p/users/{id}
允许匿名访问且不需要 CSRF 令牌。用户不存在或已停用时返回 404 Not Found。成功响应使用统一 data 包装:
{
"data": {
"userId": 2,
"userName": "alice",
"description": "后端开发者",
"avatarUrl": "/p/users/2/avatar",
"postCount": 3,
"createdAt": "2026-07-29T12:00:00"
}
}GET /p/users/{id}/avatar
返回 302 Found,Location 指向用户当前头像或默认头像。用户不存在或已停用时返回 404 Not Found。
GET /avatars/{filename}
公开访问。从配置的 kistack.data.path/avatars 目录读取文件。Content-Type 根据静态资源处理器和文件类型确定。
GET /avatars/default.png以下路由返回 Thymeleaf HTML 页面,不是 JSON 接口。
| 方法 | 路径 | 访问权限 | 页面 |
|---|---|---|---|
GET |
/login |
公开 | 登录页 |
GET |
/register |
公开 | 注册页 |
GET |
/reset-password |
公开 | 重置密码页 |
GET |
/ 或 /index |
公开 | 文章首页与搜索页 |
GET |
/posts/{postId} |
公开 | 文章详情与评论页 |
GET |
/users/{userId} |
公开 | 用户公开主页 |
GET |
/dashboard |
USER 或 ADMIN |
用户资料页 |
GET |
/editor |
USER 或 ADMIN |
新建文章编辑器 |
GET |
/editor/{postId} |
USER 或 ADMIN |
修改文章编辑器 |
GET |
/back/dashboard |
ADMIN |
后台用户管理页 |
GET /favicon.svg 是公开静态资源。
所有列表接口都使用统一 data 包装,并返回稳定分页结构:
{
"data": {
"content": [],
"page": 0,
"size": 20,
"totalElements": 0,
"totalPages": 0,
"first": true,
"last": true
}
}文章列表、搜索结果、评论和回复均固定按照 hotScore DESC、createdAt DESC、id DESC 排序。page 默认 0,size 默认 20 且最大为 100。热点文章接口使用 limit,默认 20 且最大为 50。
| 方法与路径 | 查询参数 |
|---|---|
GET /p/posts |
page、size |
GET /p/posts/hot |
limit |
GET /p/posts/search/author |
keyword、page、size |
GET /p/posts/author/{authorId} |
page、size |
GET /p/posts/search/title |
keyword、page、size |
GET /p/posts/search/tag |
keyword、page、size |
GET /p/posts/{postId} |
无 |
英文搜索忽略大小写,%、_ 和 ! 按普通字符处理。列表响应不返回完整正文;浏览单篇文章会返回正文,并在配置的去重窗口内最多为同一访问者增加一次浏览量。列表和搜索不会增加浏览量。
旧的 /p/post/pid={id}、/p/post/title={title} 等文章查询路径已经删除。
POST /u/posts 与 PUT /u/posts/{postId} 使用以下请求体:
{
"title": "Spring Data JPA notes",
"content": "Article content...",
"tags": ["spring", "jpa", "sqlite"]
}作者身份始终来自当前登录会话。标题最长 200 个字符,正文最长 100000 个字符,每篇文章必须有 1 至 10 个标签,每个标签最长 64 个字符。标签会进行 trim、转小写和去重。只有作者本人可以修改或删除文章;删除文章会同步清理评论、点赞和标签数据。
GET /p/posts/{postId}/comments 查询文章根评论,GET /p/comments/{commentId}/replies 查询某条评论的直接回复。发布评论和回复均使用:
{
"content": "评论内容"
}评论内容最长 500 个字符。回复的 postId 与 respondentId 完全由父评论推导,客户端不能指定。没有回复的评论会物理删除;已有回复的评论会保留为删除占位节点,避免回复链断裂。用户只能删除自己发布的评论。
文章和评论的点赞、取消点赞接口均为幂等操作。数据库唯一记录保证同一用户不会重复增加计数,重复取消点赞也不会产生负数。成功响应如下:
{
"data": {
"liked": true,
"likeCount": 12
}
}文章热点值由对数化的浏览、点赞、评论信号和时间衰减共同计算;评论热点值使用点赞、直接回复和时间衰减。互动发生后立即刷新热点,并由后台任务定期执行时间衰减刷新。
以下行为可以通过配置或环境变量改变,因此部署环境可能与本文档中的默认值不同:
- 初始管理员密码:
KISTACK_ADMIN_PASSWORD(必填,无默认值) - 管理员重置开关:
KISTACK_ADMIN_RESET_ENABLED(默认false) - 验证码长度:
kistack.email.verification.verification-code-length - 邮箱格式正则:
kistack.email.from.email-format-regex - 单文件大小:
spring.servlet.multipart.max-file-size - 请求总大小:
spring.servlet.multipart.max-request-size - 数据目录:
kistack.data.path - 默认头像:
kistack.data.default-avatar - 文章浏览去重窗口:
kistack.caffeine.post-view-deduplicate-minutes - 文章浏览缓存容量:
kistack.caffeine.post-view-maximum-size - 热点值刷新间隔:
kistack.hot-score.refresh-interval-ms - 热点值刷新批量大小:
kistack.hot-score.refresh-batch-size