调用随机诗词API:从调试到集成,打造你的诗词助手

引言

在传统文化数字化的浪潮中,诗词 API 成为了许多应用(如书法展示、学习打卡、智能写作辅助)的灵感来源。ApiZero 平台(极数本源)提供了简洁的随机诗词接口,开发者只需几分钟即可接入。本文将以该接口为例,详细演示从注册、调试到代码集成的完整流程,并分享生产环境下的最佳实践。

1. 准备工作:注册与获取 API Key

1.1 注册账号

访问 ApiZero 官网,点击右上角"免费注册",填写邮箱或手机号完成注册。登录后进入"控制台"。

1.2 创建应用并获取密钥

  1. 在控制台左侧菜单选择"应用管理" → "创建应用"。
  2. 填写应用名称(如"诗词助手"),选择"服务分类"为"文本",点击确认。
  3. 创建成功后,你会得到一个 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 签名生成算法

签名用于保证请求完整性。算法步骤:

  1. 将参数按字典序排序(app_key, nonce, timestamp, type, num 不包含 sign 本身)。
  2. 拼接为字符串:key1=value1&key2=value2...
  3. 在末尾追加 &secret=你的AppSecret
  4. 对拼接结果计算 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 固定包含 codemessage 字段。常见错误:

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 减少重复签名计算

如果后端需要频繁调用,可以缓存签名结果(参数不变时复用)。但注意非对称:timestampnonce 每次变化,无法直接缓存。可考虑将签名逻辑封装成函数。

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 中创建一个"每日一诗"组件。核心逻辑:

  1. 页面加载时调用后端签名接口(或直接从后端获取数据)。
  2. 展示诗词内容,可配合图片生成卡片。
  3. 提供"换一首"按钮重新请求。

示例代码(简化伪代码,实际请参考前面 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 接口设计简洁,适合快速集成到各类应用中。如果你正在开发一个文化类或教育类项目,不妨试试这个随机的诗词接口,它能为你的用户带来一丝古典的惊喜。

参考链接