本文档详细定义了 Heimdall 博客系统的完整API接口规范,包括C端(Public API)和B端(Admin API)的所有接口、参数、响应格式等。
- Admin API (B端):
https://api.example.com/api/v1/admin - Public API (C端):
https://api.example.com/api/v1/public
- 协议: HTTPS
- 方法: GET, POST, PUT, DELETE
- 编码: UTF-8
- Content-Type:
application/json
- Admin API: Bearer Token (JWT)
- Public API: 无需认证(评论提交除外)
{
"code": 200,
"message": "Success",
"data": {...},
"timestamp": "2024-01-01T12:00:00Z"
}{
"code": "validation_failed",
"msg": "请求参数验证失败",
"details": {
"field": "username",
"reason": "用户名不能为空"
},
"timestamp": "2024-01-01T12:00:00Z"
}{
"code": 200,
"message": "Success",
"data": {
"list": [...],
"pagination": {
"page": 1,
"limit": 10,
"total": 100,
"totalPages": 10,
"hasNext": true,
"hasPrev": false
}
},
"timestamp": "2024-01-01T12:00:00Z"
}POST /api/v1/admin/auth/login请求参数:
{
"username": "john-doe",
"password": "password123",
"rememberMe": false
}响应示例:
{
"code": 200,
"message": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 7200,
"user": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"username": "john-doe",
"displayName": "John Doe",
"email": "john@example.com",
"role": "admin",
"profileImage": "https://cdn.example.com/avatar.jpg"
}
}
}POST /api/v1/admin/auth/refresh请求参数:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}POST /api/v1/admin/auth/logout请求头:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
GET /api/v1/admin/auth/profilePUT /api/v1/admin/auth/profile请求参数:
{
"displayName": "John Doe Updated",
"bio": "A passionate writer and developer",
"location": "Beijing, China",
"website": "https://johndoe.com",
"twitter": "johndoe",
"facebook": "john.doe"
}PUT /api/v1/admin/auth/password请求参数:
{
"currentPassword": "oldpassword",
"newPassword": "newpassword123",
"confirmPassword": "newpassword123"
}GET /api/v1/admin/users?page=1&limit=10&role=admin&status=active响应示例:
{
"code": 200,
"message": "Success",
"data": {
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"username": "john-doe",
"displayName": "John Doe",
"email": "john@example.com",
"role": "admin",
"status": "active",
"lastLoginAt": "2024-01-01T12:00:00Z",
"createdAt": "2023-12-01T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 25,
"totalPages": 3,
"hasNext": true,
"hasPrev": false
}
}
}POST /api/v1/admin/users请求参数:
{
"username": "new-user",
"email": "newuser@example.com",
"displayName": "New User",
"role": "author",
"password": "temppassword123"
}PUT /api/v1/admin/users/{id}DELETE /api/v1/admin/users/{id}GET /api/v1/admin/security/login-logs?page=1&limit=20&userId={userId}&success=true响应示例:
{
"code": 200,
"message": "Success",
"data": {
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"userId": "60f7b1c9e1d2c3d4e5f6g7h8",
"username": "john-doe",
"ipAddress": "192.168.1.1",
"userAgent": "Mozilla/5.0...",
"success": true,
"createdAt": "2024-01-01T12:00:00Z"
}
],
"pagination": {...}
}
}GET /api/v1/admin/posts?page=1&limit=10&status=published&authorId={authorId}&tag={tagSlug}响应示例:
{
"code": 200,
"message": "Success",
"data": {
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "My First Post",
"slug": "my-first-post",
"excerpt": "This is a brief introduction...",
"featuredImage": "https://cdn.example.com/image.jpg",
"status": "published",
"type": "post",
"author": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"displayName": "John Doe",
"profileImage": "https://cdn.example.com/avatar.jpg"
},
"tags": [
{"name": "Go", "slug": "go"},
{"name": "技术", "slug": "tech"}
],
"viewCount": 1024,
"publishedAt": "2024-01-01T10:00:00Z",
"createdAt": "2023-12-30T15:00:00Z",
"updatedAt": "2024-01-01T09:00:00Z"
}
],
"pagination": {...}
}
}GET /api/v1/admin/posts/{id}响应示例:
{
"code": 200,
"message": "Success",
"data": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "My First Post",
"slug": "my-first-post",
"excerpt": "This is a brief introduction...",
"markdown": "## Title\n\nContent...",
"html": "<h2>Title</h2><p>Content...</p>",
"featuredImage": "https://cdn.example.com/image.jpg",
"type": "post",
"status": "published",
"visibility": "public",
"authorId": "60f7b1c9e1d2c3d4e5f6g7h8",
"tags": [
{"name": "Go", "slug": "go"},
{"name": "技术", "slug": "tech"}
],
"metaTitle": "Learn Go Programming - Blog",
"metaDescription": "A comprehensive guide to...",
"canonicalUrl": "https://example.com/canonical",
"readingTime": 5,
"wordCount": 1250,
"viewCount": 1024,
"publishedAt": "2024-01-01T10:00:00Z",
"createdAt": "2023-12-30T15:00:00Z",
"updatedAt": "2024-01-01T09:00:00Z"
}
}POST /api/v1/admin/posts请求参数:
{
"title": "New Post Title",
"slug": "new-post-title",
"excerpt": "Post excerpt...",
"markdown": "## Content\n\nPost content in markdown...",
"featuredImage": "https://cdn.example.com/image.jpg",
"type": "post",
"status": "draft",
"visibility": "public",
"tags": [
{"name": "Go", "slug": "go"},
{"name": "编程", "slug": "programming"}
],
"metaTitle": "SEO Title",
"metaDescription": "SEO Description",
"publishedAt": "2024-01-01T10:00:00Z"
}PUT /api/v1/admin/posts/{id}DELETE /api/v1/admin/posts/{id}POST /api/v1/admin/posts/{id}/publish请求参数:
{
"publishedAt": "2024-01-01T10:00:00Z"
}POST /api/v1/admin/posts/{id}/unpublishGET /api/v1/admin/comments?page=1&limit=20&status=pending&postId={postId}响应示例:
{
"code": 200,
"message": "Success",
"data": {
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"postId": "60f7b1c9e1d2c3d4e5f6g7h8",
"postTitle": "My First Post",
"authorName": "John Visitor",
"authorEmail": "visitor@example.com",
"content": "Great post! Very informative.",
"status": "pending",
"ipAddress": "192.168.1.100",
"likeCount": 5,
"parentId": null,
"createdAt": "2024-01-01T14:30:00Z"
}
],
"pagination": {...}
}
}PUT /api/v1/admin/comments/{id}/approvePUT /api/v1/admin/comments/{id}/rejectPUT /api/v1/admin/comments/{id}/spamDELETE /api/v1/admin/comments/{id}POST /api/v1/admin/comments/batch请求参数:
{
"action": "approve",
"commentIds": [
"60f7b1c9e1d2c3d4e5f6g7h8",
"60f7b1c9e1d2c3d4e5f6g7h9"
]
}GET /api/v1/admin/media?page=1&limit=12&type=image&uploaderId={uploaderId}响应示例:
{
"code": 200,
"message": "Success",
"data": {
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"filename": "sunset.jpg",
"url": "https://cdn.example.com/media/sunset.jpg",
"type": "image",
"mimeType": "image/jpeg",
"size": 2048576,
"width": 1920,
"height": 1080,
"alt": "Beautiful sunset",
"title": "Sunset Photo",
"uploader": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"displayName": "John Doe"
},
"createdAt": "2024-01-01T09:00:00Z"
}
],
"pagination": {...}
}
}POST /api/v1/admin/media/upload
Content-Type: multipart/form-data请求参数:
file: [文件]
alt: "Alternative text"
title: "File title"
响应示例:
{
"code": 200,
"message": "上传成功",
"data": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"filename": "uploaded-image.jpg",
"url": "https://cdn.example.com/media/uploaded-image.jpg",
"type": "image",
"mimeType": "image/jpeg",
"size": 1024768,
"width": 1280,
"height": 720
}
}PUT /api/v1/admin/media/{id}请求参数:
{
"alt": "Updated alternative text",
"title": "Updated title"
}DELETE /api/v1/admin/media/{id}GET /api/v1/admin/settings响应示例:
{
"code": 200,
"message": "Success",
"data": {
"general": {
"title": "My Awesome Blog",
"description": "A blog about technology and life",
"logo": "https://cdn.example.com/logo.png",
"timezone": "Asia/Shanghai",
"language": "zh-CN"
},
"design": {
"theme": "default",
"accentColor": "#007d9c",
"postsPerPage": 10
},
"social": {
"twitter": "myblog",
"facebook": "myblog",
"github": "myblog"
}
}
}PUT /api/v1/admin/settings请求参数:
{
"general": {
"title": "Updated Blog Title",
"description": "Updated description"
},
"design": {
"accentColor": "#ff6b6b"
}
}GET /api/v1/admin/navigation响应示例:
{
"code": 200,
"message": "Success",
"data": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"label": "首页",
"url": "/",
"order": 1,
"target": "_self",
"isActive": true
},
{
"id": "60f7b1c9e1d2c3d4e5f6g7h9",
"label": "关于",
"url": "/about",
"order": 2,
"target": "_self",
"isActive": true
}
]
}POST /api/v1/admin/navigation请求参数:
{
"label": "新菜单",
"url": "/new-page",
"order": 3,
"target": "_self",
"isActive": true
}PUT /api/v1/admin/navigation/{id}DELETE /api/v1/admin/navigation/{id}PUT /api/v1/admin/navigation/reorder请求参数:
{
"items": [
{"id": "60f7b1c9e1d2c3d4e5f6g7h8", "order": 1},
{"id": "60f7b1c9e1d2c3d4e5f6g7h9", "order": 2}
]
}GET /api/v1/admin/tags?page=1&limit=20&visibility=public响应示例:
{
"code": 200,
"message": "Success",
"data": {
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"name": "Go 语言",
"slug": "go-language",
"description": "Go编程语言相关文章",
"color": "#007d9c",
"featuredImage": "https://cdn.example.com/tag-go.png",
"postCount": 25,
"visibility": "public",
"createdAt": "2023-12-01T10:00:00Z"
}
],
"pagination": {...}
}
}POST /api/v1/admin/tags请求参数:
{
"name": "新标签",
"slug": "new-tag",
"description": "标签描述",
"color": "#ff6b6b",
"featuredImage": "https://cdn.example.com/tag.png",
"metaTitle": "SEO标题",
"metaDescription": "SEO描述",
"visibility": "public"
}PUT /api/v1/admin/tags/{id}DELETE /api/v1/admin/tags/{id}GET /api/v1/admin/analytics/dashboard响应示例:
{
"code": 200,
"message": "Success",
"data": {
"overview": {
"totalPosts": 156,
"totalComments": 423,
"totalUsers": 8,
"totalViews": 15420
},
"recentPosts": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "Latest Post",
"viewCount": 150,
"publishedAt": "2024-01-01T10:00:00Z"
}
],
"recentComments": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"content": "Great post!",
"authorName": "John",
"postTitle": "My Post",
"createdAt": "2024-01-01T14:30:00Z"
}
],
"poplarPosts": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "Popular Post",
"viewCount": 2150,
"publishedAt": "2023-12-15T10:00:00Z"
}
]
}
}GET /api/v1/admin/analytics/content?startDate=2024-01-01&endDate=2024-01-31GET /api/v1/admin/analytics/users?period=30dGET /api/v1/public/posts?page=1&limit=10&tag={tagSlug}&author={authorSlug}响应示例:
{
"code": 200,
"message": "Success",
"data": {
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "My First Post",
"slug": "my-first-post",
"excerpt": "This is a brief introduction...",
"featuredImage": "https://cdn.example.com/image.jpg",
"author": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"username": "john-doe",
"displayName": "John Doe",
"profileImage": "https://cdn.example.com/avatar.jpg",
"bio": "A passionate writer"
},
"tags": [
{"name": "Go", "slug": "go"},
{"name": "技术", "slug": "tech"}
],
"readingTime": 5,
"viewCount": 1024,
"publishedAt": "2024-01-01T10:00:00Z"
}
],
"pagination": {...}
}
}GET /api/v1/public/posts/{slug}响应示例:
{
"code": 200,
"message": "Success",
"data": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "My First Post",
"slug": "my-first-post",
"excerpt": "This is a brief introduction...",
"html": "<h2>Title</h2><p>Content...</p>",
"featuredImage": "https://cdn.example.com/image.jpg",
"author": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"username": "john-doe",
"displayName": "John Doe",
"profileImage": "https://cdn.example.com/avatar.jpg",
"bio": "A passionate writer and developer",
"location": "Beijing, China",
"website": "https://johndoe.com",
"twitter": "johndoe"
},
"tags": [
{"name": "Go", "slug": "go"},
{"name": "技术", "slug": "tech"}
],
"metaTitle": "Learn Go Programming - Blog",
"metaDescription": "A comprehensive guide to...",
"canonicalUrl": "https://example.com/posts/my-first-post",
"readingTime": 5,
"wordCount": 1250,
"viewCount": 1024,
"publishedAt": "2024-01-01T10:00:00Z",
"updatedAt": "2024-01-01T09:00:00Z"
}
}GET /api/v1/public/pages/{slug}GET /api/v1/public/posts/popular?limit=5响应示例:
{
"code": 200,
"message": "Success",
"data": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "Popular Post Title",
"slug": "popular-post",
"featuredImage": "https://cdn.example.com/image.jpg",
"viewCount": 5420,
"publishedAt": "2023-12-15T10:00:00Z"
}
]
}GET /api/v1/public/posts/recent?limit=5GET /api/v1/public/posts/{postId}/related?limit=3GET /api/v1/public/search?q={keyword}&page=1&limit=10响应示例:
{
"code": 200,
"message": "Success",
"data": {
"query": "Go语言",
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "Go语言入门教程",
"slug": "go-tutorial",
"excerpt": "这是一篇关于Go语言的入门教程...",
"featuredImage": "https://cdn.example.com/go.jpg",
"author": {
"displayName": "John Doe"
},
"tags": [{"name": "Go", "slug": "go"}],
"publishedAt": "2024-01-01T10:00:00Z",
"relevance": 0.95
}
],
"pagination": {...}
}
}GET /api/v1/public/tags响应示例:
{
"code": 200,
"message": "Success",
"data": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"name": "Go 语言",
"slug": "go-language",
"description": "Go编程语言相关文章",
"color": "#007d9c",
"featuredImage": "https://cdn.example.com/tag-go.png",
"postCount": 25
}
]
}GET /api/v1/public/tags/{slug}GET /api/v1/public/authors响应示例:
{
"code": 200,
"message": "Success",
"data": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"username": "john-doe",
"displayName": "John Doe",
"profileImage": "https://cdn.example.com/avatar.jpg",
"bio": "A passionate writer and developer",
"location": "Beijing, China",
"website": "https://johndoe.com",
"twitter": "johndoe",
"postCount": 15
}
]
}GET /api/v1/public/authors/{username}GET /api/v1/public/archives响应示例:
{
"code": 200,
"message": "Success",
"data": [
{
"year": 2024,
"months": [
{
"month": 1,
"postCount": 5,
"posts": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"title": "January Post",
"slug": "january-post",
"publishedAt": "2024-01-15T10:00:00Z"
}
]
}
]
}
]
}GET /api/v1/public/posts/{postId}/comments?page=1&limit=10响应示例:
{
"code": 200,
"message": "Success",
"data": {
"list": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"authorName": "John Visitor",
"authorUrl": "https://johnvisitor.com",
"content": "<p>Great post! Very informative.</p>",
"likeCount": 5,
"createdAt": "2024-01-01T14:30:00Z",
"replies": [
{
"id": "60f7b1c9e1d2c3d4e5f6g7h9",
"authorName": "Author",
"content": "<p>Thank you for your feedback!</p>",
"createdAt": "2024-01-01T15:00:00Z"
}
]
}
],
"pagination": {...}
}
}POST /api/v1/public/posts/{postId}/comments请求参数:
{
"authorName": "John Visitor",
"authorEmail": "visitor@example.com",
"authorUrl": "https://johnvisitor.com",
"content": "Great post! Very informative.",
"parentId": null
}响应示例:
{
"code": 200,
"message": "评论已提交,等待审核",
"data": {
"id": "60f7b1c9e1d2c3d4e5f6g7h8",
"status": "pending"
}
}POST /api/v1/public/comments/{commentId}/likeDELETE /api/v1/public/comments/{commentId}/likeGET /api/v1/public/site/info响应示例:
{
"code": 200,
"message": "Success",
"data": {
"title": "My Awesome Blog",
"description": "A blog about technology and life",
"logo": "https://cdn.example.com/logo.png",
"language": "zh-CN",
"timezone": "Asia/Shanghai",
"social": {
"twitter": "myblog",
"facebook": "myblog",
"github": "myblog"
}
}
}GET /api/v1/public/navigation响应示例:
{
"code": 200,
"message": "Success",
"data": [
{
"label": "首页",
"url": "/",
"target": "_self"
},
{
"label": "关于",
"url": "/about",
"target": "_self"
}
]
}GET /api/v1/public/sitemap.xml
Content-Type: application/xml响应示例:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://example.com/posts/my-first-post</loc>
<lastmod>2024-01-01T10:00:00Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
</urlset>GET /api/v1/public/rss.xml
Content-Type: application/rss+xmlGET /api/v1/public/atom.xml
Content-Type: application/atom+xmlGET /api/v1/public/feed.json
Content-Type: application/json| 错误码 | 说明 | 示例场景 |
|---|---|---|
| 200 | 成功 | 请求正常处理 |
| 400 | 请求参数错误 | 参数验证失败 |
| 401 | 未认证 | Token无效或过期 |
| 403 | 权限不足 | 无权限访问资源 |
| 404 | 资源不存在 | 文章、用户不存在 |
| 409 | 资源冲突 | 用户名、邮箱已存在 |
| 422 | 实体无法处理 | 数据格式正确但逻辑错误 |
| 429 | 请求过于频繁 | 触发限流 |
| 500 | 服务器内部错误 | 系统异常 |
- 使用URL路径版本控制:
/v1/admin/posts - 主要版本变更时创建新的API版本
- 向后兼容的小版本变更保持同一版本号
- 新版本发布后,旧版本至少维护6个月
- 通过响应头
X-API-Version标识当前版本 - 通过响应头
X-API-Deprecated标识弃用版本
注意: 本API规范基于RESTful设计原则,所有接口均返回JSON格式数据。实际开发中可能会根据具体需求进行调整。