flask调用短信api发验证码需直接用requests.post()调云厂商https接口,设timeout=(3,8)、content-type: application/json并用json=参数;验证码存sqlite(phone/code/created_at),按服务端utc时间校验并立即删除;按手机号用redis限流(incr+expire),防刷;模板与签名须审核通过,号码带+86前缀。

Flask里怎么调用短信API发验证码
直接上手:用 requests.post() 调云厂商的 HTTPS 接口,别碰任何“封装 SDK”,除非你确认它不偷偷改请求头、不强制加全局 session、不屏蔽底层错误码。
常见错误现象:401 Unauthorized(密钥写错或没配签名)、400 Bad Request(参数名拼错,比如把 phone_number 写成 mobile)、ConnectionTimeout(没设 timeout=(3, 8),卡住整个 Flask 请求线程)。
- 必须显式设置
timeout,推荐(3, 8):3 秒连通,8 秒读响应,超时就抛异常,别等默认的永远不返回 - 云厂商接口大多要求
Content-Type: application/json,且 body 是 JSON 字符串,不是 form 表单 —— 别用data=xxx,要用json=xxx参数让requests自动序列化+设 header - 验证码内容必须提前在云平台配置模板,API 调用时只传
template_id和template_param,别试图在代码里拼接短信正文
验证码怎么存、存多久、怎么校验
别用内存字典(dict)存验证码,重启服务就丢;也别一上来就上 Redis —— 如果只是小项目、低频使用,SQLite 就够用,关键是控制好过期逻辑。
使用场景:用户注册/登录/换绑手机号,每次请求生成新验证码,旧的自动失效(不是覆盖,是删掉)。
- 存储结构建议两字段:
phone(主键或索引)、code、created_at(datetime 或 timestamp),查的时候加WHERE phone = ? AND created_at > datetime('now', '-5 minutes') - 校验时先查,再比对,比对成功立刻删掉该记录(防重放),不要“查+更新状态”,避免并发下重复验证
- 别依赖客户端传来的“时间戳”判断是否过期,全以服务端时间为准;数据库字段用 UTC 时间,避免本地时区混乱
Flask路由里怎么防刷和限流
没限流的短信接口等于敞开的水龙头,一个脚本就能把你余额刷光。云厂商的 QPS 限制是兜底,不是第一道防线。
参数差异:按 IP 限?按手机号限?还是两者叠加?生产环境必须按手机号限(IP 可伪造,手机号才是真实瓶颈点)。
- 用 Redis 实现简单计数器:
INCR sms:limit:{phone}+EXPIRE sms:limit:{phone} 60,单手机号 60 秒内最多 3 次,超了直接return jsonify({"error": "操作太频繁"}), 429 - 别在数据库里做“查总数再判断”,高并发下会漏判;Redis 的原子操作才是可靠选择
- 注意 Redis 连接池配置,别每个请求都新建连接,
redis.Redis(connection_pool=pool)是基本操作
调试时收不到短信但接口返回 success
这是最让人抓狂的情况:HTTP 状态码 200,云厂商返回 {"code": 0, "msg": "OK"},但手机就是没响。问题几乎全出在模板审核、签名绑定、号码格式上。
性能影响:这类问题不会拖慢接口,但会让前端以为“发成功了”,用户干等,客服电话打爆。
- 检查模板是否“已审核通过”,草稿/待审状态的模板调用会静默失败(云厂商返回 success,但实际没发)
- 确认签名是否已在该模板中绑定,且签名本身也已审核通过 —— 两个环节缺一不可
- 手机号必须带国际区号,如
+8613812345678,少个+或写成0086都可能被过滤,具体看厂商文档,别猜 - 测试阶段用自己实名认证过的号码,虚拟号段(如 170/171)或部分物联网卡大概率被拦截
复杂点在于:每个云厂商的错误码语义不一致,有的把模板未审核归为 1015,有的是 20002,还有的干脆不报错。真出问题,得翻他们控制台里的“发送日志”,而不是只信 API 返回体。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











