引言
在传统文化数字化的浪潮中,诗词 API 成为了许多应用(如书法展示、学习打卡、智能写作辅助)的灵感来源。ApiZero 平台(极数本源)提供了简洁的随机诗词接口,开发者只需几分钟即可接入。本文将以该接口为例,详细演示从注册、调试到代码集成的完整流程,并分享生产环境下的最佳实践。
1. 准备工作:注册与获取 API Key
1.1 注册账号
访问 ApiZero 官网,点击右上角"免费注册",填写邮箱或手机号完成注册。登录后进入"控制台"。
1.2 创建应用并获取密钥
- 在控制台左侧菜单选择"应用管理" → "创建应用"。
- 填写应用名称(如"诗词助手"),选择"服务分类"为"文本",点击确认。
- 创建成功后,你会得到一个 AppKey (如
a1b2c3d4e5f6)和 AppSecret (如sk-xxxxxxxxx)。请妥善保管,后续请求需要携带。
注意 :ApiZero 使用 AppKey + 签名 认证方式,并非简单的 Bearer Token。签名算法详见第 3 节。
2. 接口概览与在线调试
2.1 接口信息
- 端点 :
https://api.apizero.cn/v1/shici/random - HTTP 方法:GET
- 请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_key |
string | 是 | 应用公钥 |
timestamp |
int | 是 | 当前 Unix 时间戳(秒) |
nonce |
string | 是 | 随机字符串,防重放 |
sign |
string | 是 | 签名,见下文 |
type |
string | 否 | 诗词类型,如"唐诗""宋词",不传则随机 |
num |
int | 否 | 返回数量(1-10),默认 1 |
2.2 签名生成算法
签名用于保证请求完整性。算法步骤:
- 将参数按字典序排序(
app_key,nonce,timestamp,type,num不包含sign本身)。 - 拼接为字符串:
key1=value1&key2=value2...。 - 在末尾追加
&secret=你的AppSecret。 - 对拼接结果计算 MD5(32位小写)即为 sign。
示例(伪代码):
text
params = {
"app_key": "a1b2c3d4e5f6",
"timestamp": 1717574400,
"nonce": "abc123",
"type": "唐诗",
"num": 1
}
sorted_str = "app_key=a1b2c3d4e5f6&nonce=abc123&num=1×tamp=1717574400&type=唐诗"
raw = sorted_str + "&secret=sk-xxxxxxxxx"
sign = md5(raw) # 结果如 "e99a18c428cb38d5f260853678922e03"
2.3 在线调试(可选)
ApiZero 平台提供"在线调试"功能,无需写代码即可测试接口。进入接口详情页,填写参数后点击"发送",查看响应是否符合预期。这一步能快速验证签名与参数正确性。
3. 代码集成:JavaScript(浏览器/Node.js)
3.1 使用 Fetch 发起请求(浏览器)
javascript
// 在浏览器中运行,注意避免泄露 AppSecret(应放在后端)
const APP_KEY = 'a1b2c3d4e5f6';
const APP_SECRET = 'sk-xxxxxxxxx'; // 实际开发中请使用后端签名
function generateSign(params, secret) {
const keys = Object.keys(params).sort();
const sortedStr = keys.map(k => `${k}=${params[k]}`).join('&');
const raw = sortedStr + '&secret=' + secret;
// 使用 crypto-js 或内置 crypto.subtle 计算 MD5
// 这里用伪代码示意
return md5(raw);
}
async function fetchRandomPoem(type, num = 1) {
const timestamp = Math.floor(Date.now() / 1000);
const nonce = Math.random().toString(36).substring(2, 10);
const params = {
app_key: APP_KEY,
timestamp,
nonce,
type,
num
};
params.sign = generateSign(params, APP_SECRET);
const query = new URLSearchParams(params).toString();
const url = `https://api.apizero.cn/v1/shici/random?${query}`;
try {
const response = await fetch(url);
const data = await response.json();
if (data.code === 200) {
return data.data;
} else {
throw new Error(data.message);
}
} catch (error) {
console.error('请求失败:', error);
return null;
}
}
// 使用示例
fetchRandomPoem('唐诗', 1).then(poem => {
if (poem) {
console.log(`${poem.title} --- ${poem.author}\n${poem.content}`);
}
});
注意:浏览器端直接暴露 AppSecret 危险,建议将签名逻辑放在后端,前端仅传递参数并获取已签名的 URL。
3.2 Node.js 示例(使用 axios)
javascript
const axios = require('axios');
const crypto = require('crypto');
const APP_KEY = 'a1b2c3d4e5f6';
const APP_SECRET = 'sk-xxxxxxxxx';
function generateSign(params, secret) {
const keys = Object.keys(params).sort();
const sortedStr = keys.map(k => `${k}=${params[k]}`).join('&');
const raw = sortedStr + '&secret=' + secret;
return crypto.createHash('md5').update(raw).digest('hex');
}
async function getRandomPoem(type, num = 1) {
const timestamp = Math.floor(Date.now() / 1000);
const nonce = crypto.randomBytes(4).toString('hex');
const params = {
app_key: APP_KEY,
timestamp,
nonce,
type,
num
};
params.sign = generateSign(params, APP_SECRET);
try {
const response = await axios.get('https://api.apizero.cn/v1/shici/random', { params });
if (response.data.code === 200) {
return response.data.data;
} else {
console.error('API 错误:', response.data.message);
return null;
}
} catch (error) {
console.error('网络异常:', error.message);
return null;
}
}
// 调用
getRandomPoem('宋词', 2).then(poems => {
if (poems && poems.length > 0) {
poems.forEach(p => console.log(`${p.title} --- ${p.author}`));
}
});
4. 代码集成:Python(requests)
python
import hashlib
import time
import random
import string
import requests
APP_KEY = 'a1b2c3d4e5f6'
APP_SECRET = 'sk-xxxxxxxxx'
def generate_sign(params, secret):
# 按字典序排序并拼接
sorted_keys = sorted(params.keys())
sorted_str = '&'.join([f'{k}={params[k]}' for k in sorted_keys])
raw = sorted_str + '&secret=' + secret
return hashlib.md5(raw.encode('utf-8')).hexdigest()
def get_random_poem(poem_type=None, num=1):
timestamp = int(time.time())
nonce = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
params = {
'app_key': APP_KEY,
'timestamp': timestamp,
'nonce': nonce,
'num': num
}
if poem_type:
params['type'] = poem_type
params['sign'] = generate_sign(params, APP_SECRET)
url = 'https://api.apizero.cn/v1/shici/random'
try:
resp = requests.get(url, params=params, timeout=5)
data = resp.json()
if data.get('code') == 200:
return data['data']
else:
print(f'API error: {data.get("message")}')
return None
except Exception as e:
print(f'Request failed: {e}')
return None
# 示例
if __name__ == '__main__':
poem = get_random_poem('宋词', 1)
if poem:
print(f"{poem['title']}\n{poem['author']}\n{poem['content']}")
5. 错误处理与状态码
接口返回的 JSON 固定包含 code 和 message 字段。常见错误:
| code | message | 原因 | 解决 |
|---|---|---|---|
| 200 | Success | 正常 | --- |
| 400 | Invalid app_key | AppKey 格式错误 | 检查控制台中的 AppKey |
| 401 | Signature verification failed | 签名不正确 | 重新检查签名算法、时间戳准确性 |
| 402 | Timestamp expired | 时间戳偏差超过5分钟 | 同步服务器时间,确保 NTP 同步 |
| 403 | Replay attack detected | nonce 重复 | 确保每次 nonce 随机唯一 |
| 429 | Too many requests | 超出调用配额 | 降低频率或升级套餐 |
| 500 | Internal server error | 服务器错误 | 请联系平台支持 |
防御性编程建议 :调用方应解析 code 并进行相应处理,而非仅凭 HTTP 状态码。
6. 性能优化与缓存策略
6.1 减少重复签名计算
如果后端需要频繁调用,可以缓存签名结果(参数不变时复用)。但注意非对称:timestamp 和 nonce 每次变化,无法直接缓存。可考虑将签名逻辑封装成函数。
6.2 前端局部缓存
对于不要求实时更新的场景(如每日一言),可在前端/客户端缓存当日诗词,避免重复请求。
javascript
// 简单缓存示例(localStorage)
const CACHE_KEY = 'poem_cache';
const CACHE_EXPIRY = 3600 * 1000; // 1小时
function getCachedPoem() {
const cached = localStorage.getItem(CACHE_KEY);
if (cached) {
const parsed = JSON.parse(cached);
if (Date.now() - parsed.timestamp < CACHE_EXPIRY) {
return parsed.poem;
}
}
return null;
}
6.3 并发控制
若有多处同时调用同一接口,可使用防抖或请求合并,避免瞬间打满配额。
7. 安全最佳实践
- 绝不泄露 AppSecret:前端代码中禁止出现明文 Secret,应通过后端代理签名。
- 使用 HTTPS:防止中间人篡改签名。
- 参数校验:在前端也做简单合法性检查(如 num 范围)。
- 限流熔断:在客户端做重试退避(Exponential Backoff)。
8. 完整示例:一键生成诗句卡片
结合上述内容,我们可以在 Vue/React 中创建一个"每日一诗"组件。核心逻辑:
- 页面加载时调用后端签名接口(或直接从后端获取数据)。
- 展示诗词内容,可配合图片生成卡片。
- 提供"换一首"按钮重新请求。
示例代码(简化伪代码,实际请参考前面 fetch 用法):
jsx
// React 组件
import React, { useState, useEffect } from 'react';
function PoemCard() {
const [poem, setPoem] = useState(null);
const fetchPoem = async () => {
const res = await fetch('/api/poem'); // 后端代理
const data = await res.json();
setPoem(data);
};
useEffect(() => { fetchPoem(); }, []);
return (
<div className="poem-card">
{poem && (
<>
<h3>{poem.title}</h3>
<p className="author">{poem.author}</p>
<div className="content">{poem.content}</div>
</>
)}
<button onClick={fetchPoem}>换一首</button>
</div>
);
}
总结
通过本文,你应该已经掌握了使用 ApiZero 随机诗词 API 的完整流程:从注册获取密钥、理解签名算法、在线调试,到在 JavaScript 和 Python 中集成,并考虑了错误处理、缓存和安全。这个 API 接口设计简洁,适合快速集成到各类应用中。如果你正在开发一个文化类或教育类项目,不妨试试这个随机的诗词接口,它能为你的用户带来一丝古典的惊喜。
参考链接: