API 使用教程
从零开始接入 留言板API。本教程将带你完成密钥获取、接口认证、留言提交、列表查询等全部操作,并提供 cURL、JavaScript、Python、PHP 示例代码。
获取密钥
在后台创建 API Key,配置域名白名单
理解认证
掌握 URL 参数与 JSON Body 两种传参方式
调用接口
提交留言、获取列表、回复互动
接入上线
处理错误、优化性能、保障安全
准备工作
在调用 API 之前,你需要完成以下三步准备工作。这些步骤确保你的请求能够被正确识别和授权。
-
访问后台管理
打开 后台管理 页面,使用管理员账号登录。如果你是第一次使用,请先运行
install.php完成系统安装。 - 创建 API Key 进入「API 管理」→「新增 API Key」,填写一个便于识别的名称(如"我的博客")。系统会生成一串唯一的密钥,请妥善保存。
-
配置域名白名单
在 API Key 详情中设置允许调用的域名。支持精确匹配
example.com和通配符*.example.com。留空表示不限制,但强烈建议填写,防止密钥泄露后被滥用。
认证与 Base URL
所有接口(除全局统计外)都需要认证。API 使用统一的入口地址,并通过 api_key 参数识别调用者身份。
https://ly.wzu.me/api.phpapi_key两种传参方式
API 支持两种方式传递 api_key 和其他参数,你可以根据场景选择最合适的方式。
方式一:URL 参数(推荐用于 GET 请求)
将参数附加在 URL 末尾,适合浏览器直接访问和简单的 GET 查询。
GET https://ly.wzu.me/api.php?action=verify&api_key=YOUR_KEY
方式二:JSON Body(推荐用于 POST 请求)
将参数放在请求体中,需设置请求头 Content-Type: application/json。适合提交留言、回复等写操作。
{
"api_key": "YOUR_KEY",
"nickname": "小明",
"content": "留言内容"
}
通用响应格式
无论调用哪个接口,成功或失败都会返回统一的 JSON 结构:
code业务状态码,200 表示成功,其他值为失败msg可读提示信息,用于错误排查和展示data业务数据,不同接口返回不同结构;失败时可能为空发送第一个请求
下面使用验证接口测试你的 API Key 是否有效。这是最简单的接口,只需一个 api_key 参数。
测试 API Key 是否有效,同时返回该密钥的备注名称。建议接入前先调用此接口确认密钥配置正确。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| api_key | string | 必填 | 在后台生成的 API Key |
调用示例
curl "https://ly.wzu.me/api.php?action=verify&api_key=YOUR_KEY"
fetch('https://ly.wzu.me/api.php?action=verify&api_key=YOUR_KEY')
.then(res => res.json())
.then(data => console.log(data));
import requests url = 'https://ly.wzu.me/api.php?action=verify&api_key=YOUR_KEY' res = requests.get(url) print(res.json())
$res = file_get_contents('https://ly.wzu.me/api.php?action=verify&api_key=YOUR_KEY');
$data = json_decode($res, true);
print_r($data);
成功响应
常见错误响应
提交留言
提交一条新留言。系统会自动识别 IP 归属地、设备信息,并进行敏感词过滤、垃圾检测和 IP 限流。
向指定 API Key 对应的留言空间提交一条留言。留言提交后需要管理员在后台审核通过,才会显示在列表中。
请求参数
| 参数 | 类型 | 必填 | 限制 | 说明 |
|---|---|---|---|---|
| api_key | string | 必填 | - | API 密钥 |
| nickname | string | 必填 | ≤ 30 字符 | 留言者昵称 |
| content | string | 必填 | ≤ 500 字符 | 留言内容 |
| string | 可选 | 合法邮箱 | 填写后若有人回复,系统会自动发送邮件通知 |
400;如果被识别为垃圾信息,同样返回 400。建议前端提示用户修改后重试。
调用示例
curl -X POST "https://ly.wzu.me/api.php?action=post" \
-H "Content-Type: application/json" \
-d '{
"api_key": "YOUR_KEY",
"nickname": "小明",
"content": "这个功能非常好用!",
"email": "xiaoming@example.com"
}'
fetch('https://ly.wzu.me/api.php?action=post', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: 'YOUR_KEY',
nickname: '小明',
content: '这个功能非常好用!',
email: 'xiaoming@example.com'
})
})
.then(res => res.json())
.then(data => {
if (data.code === 200) {
console.log('留言成功,ID:', data.data.id);
} else {
console.error('提交失败:', data.msg);
}
});
import requests
url = 'https://ly.wzu.me/api.php?action=post'
payload = {
'api_key': 'YOUR_KEY',
'nickname': '小明',
'content': '这个功能非常好用!',
'email': 'xiaoming@example.com'
}
res = requests.post(url, json=payload)
print(res.json())
$ch = curl_init('https://ly.wzu.me/api.php?action=post');
$payload = json_encode([
'api_key' => 'YOUR_KEY',
'nickname' => '小明',
'content' => '这个功能非常好用!',
'email' => 'xiaoming@example.com'
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$res = curl_exec($ch);
print_r(json_decode($res, true));
成功响应
常见错误响应
回复留言
以访客身份回复某条留言,支持多轮互动。回复内容同样会经过敏感词过滤。
回复一条已存在且审核通过的留言。如果留言者填写了邮箱,系统会自动发送邮件通知。回复不会立即出现在列表中,需要管理员审核后随主留言一起展示。
请求参数
| 参数 | 类型 | 必填 | 限制 | 说明 |
|---|---|---|---|---|
| api_key | string | 必填 | - | API 密钥 |
| message_id | int | 必填 | - | 被回复的留言 ID |
| nickname | string | 必填 | ≤ 30 字符 | 回复者昵称 |
| content | string | 必填 | ≤ 500 字符 | 回复内容 |
| string | 可选 | 合法邮箱 | 回复者邮箱 |
调用示例
curl -X POST "https://ly.wzu.me/api.php?action=reply" \
-H "Content-Type: application/json" \
-d '{
"api_key": "YOUR_KEY",
"message_id": 42,
"nickname": "小红",
"content": "我也觉得很好用!"
}'
fetch('https://ly.wzu.me/api.php?action=reply', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: 'YOUR_KEY',
message_id: 42,
nickname: '小红',
content: '我也觉得很好用!'
})
})
.then(res => res.json())
.then(data => console.log(data));
import requests
url = 'https://ly.wzu.me/api.php?action=reply'
payload = {
'api_key': 'YOUR_KEY',
'message_id': 42,
'nickname': '小红',
'content': '我也觉得很好用!'
}
res = requests.post(url, json=payload)
print(res.json())
$ch = curl_init('https://ly.wzu.me/api.php?action=reply');
$payload = json_encode([
'api_key' => 'YOUR_KEY',
'message_id' => 42,
'nickname' => '小红',
'content' => '我也觉得很好用!'
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
print_r(json_decode(curl_exec($ch), true));
成功响应
常见错误响应
留言列表
分页获取已审核通过的留言,包含 IP 归属地、设备信息、管理员回复以及最新访客回复。
获取当前 API Key 对应的留言空间中的已审核留言列表。支持分页,每页最多 50 条。返回结果包含管理员最新回复和最近访客回复列表。
请求参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| api_key | string | 必填 | - | API 密钥 |
| page | int | 可选 | 1 | 页码,从 1 开始 |
| per_page | int | 可选 | 10 | 每页数量,最大 50 |
调用示例
curl "https://ly.wzu.me/api.php?action=list&api_key=YOUR_KEY&page=1&per_page=10"
const params = new URLSearchParams({
action: 'list',
api_key: 'YOUR_KEY',
page: '1',
per_page: '10'
});
fetch(`https://ly.wzu.me/api.php?${params}`)
.then(res => res.json())
.then(data => {
if (data.code === 200) {
data.data.messages.forEach(msg => console.log(msg));
}
});
import requests
params = {
'action': 'list',
'api_key': 'YOUR_KEY',
'page': 1,
'per_page': 10
}
res = requests.get('https://ly.wzu.me/api.php', params=params)
print(res.json())
$url = 'https://ly.wzu.me/api.php?action=list&api_key=YOUR_KEY&page=1&per_page=10'; $res = file_get_contents($url); print_r(json_decode($res, true));
成功响应
返回字段说明
total该 API Key 下的留言总数page当前请求的页码per_page每页返回数量total_pages总页数messages留言对象数组messages[].id留言唯一 IDmessages[].nickname留言者昵称messages[].content留言内容messages[].city根据 IP 解析的归属地messages[].device_info设备、操作系统、浏览器信息messages[].created_at留言创建时间messages[].replies回复列表,无回复时为空数组replies[].nickname回复者昵称replies[].content回复内容replies[].is_admin是否管理员回复(1=是,0=否)留言详情
根据留言 ID 获取单条留言的完整信息,包含所有回复(管理员回复和用户回复)。
当你需要展示单条留言详情页,或者从列表点击某条留言查看完整内容时,可以使用此接口。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| api_key | string | 必填 | API 密钥 |
| id | int | 必填 | 留言 ID |
调用示例
curl "https://ly.wzu.me/api.php?action=detail&api_key=YOUR_KEY&id=42"
fetch('https://ly.wzu.me/api.php?action=detail&api_key=YOUR_KEY&id=42')
.then(res => res.json())
.then(data => console.log(data));
成功响应
留言统计
获取当前 API Key 下的留言总数和今日新增数量。
适用于在网站侧边栏展示"已有 XX 条留言"、"今日 XX 条留言"等统计信息。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| api_key | string | 必填 | API 密钥 |
调用示例
curl "https://ly.wzu.me/api.php?action=count&api_key=YOUR_KEY"
成功响应
全局统计
获取所有 API Key 的总调用次数、留言总数和活跃站点数。此接口无需认证,可公开调用。
通常用于首页展示平台级数据,如总 API 调用次数、总留言数、活跃站点数。
api_key,可直接通过浏览器或前端调用。首页的实时统计数字就是通过此接口获取的。
调用示例
curl "https://ly.wzu.me/api.php?action=stats"
成功响应
完整接入流程
下面展示如何从零开始,在你的网页中嵌入一个完整的留言板功能。
创建 HTML 结构
准备一个表单用于提交留言,一个列表区域用于展示留言。
<form id="msgForm"> <input type="text" id="nickname" placeholder="昵称" required> <textarea id="content" placeholder="留言内容" required></textarea> <button type="submit">提交留言</button> </form> <div id="msgList">加载中...</div>
加载留言列表
页面加载时调用 action=list 接口,将返回的留言渲染到页面中。
const API_KEY = 'YOUR_KEY';
const API_BASE = 'https://ly.wzu.me/api.php';
async function loadMessages(page = 1) {
const res = await fetch(`${API_BASE}?action=list&api_key=${API_KEY}&page=${page}&per_page=10`);
const json = await res.json();
if (json.code !== 200) return alert('加载失败:' + json.msg);
const list = document.getElementById('msgList');
list.innerHTML = json.data.messages.map(m => `
<div class="msg-item">
<strong>${m.nickname}</strong> <span>${m.city}</span>
<p>${m.content}</p>
<small>${m.created_at}</small>
${(m.replies || []).map(r =>
r.is_admin
? `<p style="color:#6366f1">管理员回复:${r.content}</p>`
: `<p style="color:#06b6d4">${r.nickname} 回复:${r.content}</p>`
).join('')}
</div>
`).join('');
}
loadMessages();
提交新留言
监听表单提交事件,调用 action=post 接口,提交成功后重新加载列表。
document.getElementById('msgForm').addEventListener('submit', async (e) => {
e.preventDefault();
const res = await fetch(`${API_BASE}?action=post`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: API_KEY,
nickname: document.getElementById('nickname').value,
content: document.getElementById('content').value
})
});
const json = await res.json();
if (json.code === 200) {
alert('留言提交成功,等待审核');
loadMessages();
} else {
alert('提交失败:' + json.msg);
}
});
错误处理
了解每个错误码的含义,并编写对应的处理逻辑,可以让你的应用更健壮。
| 状态码 | 含义 | 建议处理 |
|---|---|---|
| 200 | 请求成功 | 正常处理返回数据 |
| 400 | 参数错误、内容超长、包含敏感词或垃圾信息 | 提示用户检查输入内容,修改后重试 |
| 401 | 缺少 api_key 参数 | 检查请求是否携带了 api_key |
| 403 | api_key 无效、已禁用或当前域名不在白名单 | 检查密钥是否正确,域名白名单是否配置 |
| 404 | 留言不存在或 action 参数未知 | 检查 ID 是否正确,action 是否拼写正确 |
| 405 | 请求方法不允许 | 确认 POST 接口使用 POST,GET 接口使用 GET |
| 429 | 请求过于频繁,触发 IP 限流 | 等待一段时间后重试,建议增加前端提交间隔 |
async function apiCall(url, options = {}) {
const res = await fetch(url, options);
const json = await res.json();
if (json.code === 200) return json.data;
switch (json.code) {
case 400: throw new Error('内容格式错误:' + json.msg);
case 401: throw new Error('缺少 API Key');
case 403: throw new Error('权限不足:' + json.msg);
case 429: throw new Error('操作过于频繁,请稍后再试');
default: throw new Error(json.msg);
}
}
最佳实践
遵循以下建议,确保 API 调用的安全性、稳定性和用户体验。
*.example.com。即使密钥被泄露,没有正确的域名也无法调用。
常见问题
接入过程中经常遇到的问题及解答。
www 或端口号)。Access-Control-Allow-Origin: *,支持跨域。但跨域请求仍会受域名白名单限制,请确保白名单已配置。ip_rate_limit_seconds 的值。