让 Django 验证码“动”起来:深度解析验证码动态刷新的前后端闭环

让 Django 验证码“动”起来:深度解析验证码动态刷新的前后端闭环

在前后端分离与用户体验至上的今天,一个“点击刷新验证码”的小功能,却常常让开发者踩遍 404、CORS、AJAX 验证等坑。本文带你从模板语法到 Django 源码,彻底拆解 django-simple-captcha 动态刷新的正确姿势,并给出“不升级库”与“升级库”两套实战方案,让你十分钟内拥有丝滑验证码体验。


1. 需求缘起:为什么要“无刷新”验证码?

  • 用户体验:整页刷新会丢失表单已填写内容,增加跳出率。
  • 安全考量:验证码使用一次即失效,必须异步拉取新图片。
  • 前后端分离:越来越普遍的场景下,需要纯 API 方式换取新验证码。

2. 技术栈与版本信息

组件 版本(本文示例) 备注
Django 3.2+ 4.x 亦适用
django-simple-captcha 0.5.x(旧版) 0.5.17 之前强制 AJAX
前端 原生 JS 无需框架,Vue/React 同理

3. 路由匹配:404 不一定是“没找到”

很多开发者看到 Page not found 就以为 URL 写错,实则 Django 已经正确匹配:

captcha/ refresh/$ [name='captcha-refresh']

视图内部主动 raise Http404,罪魁祸首是:

# captcha/views.py(0.5.x)
if not request.headers.get("x-requested-with") == "XMLHttpRequest":
    raise Http404

旧版库只允许 AJAX 请求,否则一律 404。


4. 前端代码:两行 header 搞定刷新

// 1. 绑定刷新按钮
document.getElementById('refresh-captcha').addEventListener('click', e => {
  e.preventDefault();

  // 2. 请求新验证码
  fetch("/captcha/refresh/", {
    method: "GET",
    headers: {"X-Requested-With": "XMLHttpRequest"} // 关键!
  })
  .then(r => r.json())
  .then(data => {
    // 3. 更新图片与隐藏 key
    document.querySelector('img.captcha').src = data.image_url;
    document.querySelector('input[name="captcha_0"]').value = data.key;
  })
  .catch(console.error);
});

模板片段(register.html):

<div class="form-group">
  <label>验证码</label>
  {{ form.captcha }}
  <a href="#" id="refresh-captcha">看不清?点击刷新</a>
</div>

5. 后端验证:无需改动,即插即用

django-simple-captcha 的 CaptchaField 会自动把 captcha_0(key)与 captcha_1(用户输入)拿去校验,前端只负责换图与换 key,后端零成本。


6. 升级派:一劳永逸的“官方推荐”

pip install -U django-simple-captcha

新版本已移除 is_ajax() 判断,普通 GET 也能 200,前端可直接:

fetch("/captcha/refresh/").then(res => res.json())...

升级后别忘了:

python manage.py migrate  # 0.5→0.6 字段有变动
pip freeze > requirements.txt

7. 常见坑位速查表

现象 根因 快速修复
404 且路由已匹配 缺少 X-Requested-With 加 header 或升级库
CORS 报错 前端端口≠后端端口 corsheaders 或同域代理
刷新后验证失败 隐藏 key 未更新 data.key 写回 captcha_0
图片缓存 浏览器复用旧图 image_url 后拼时间戳(官方 URL 已带随机 key,一般无需)

8. 延伸:如何“本地重写”刷新接口(不想升级)

在项目内新建 my captcha/views.py

from captcha.views import captcha_refresh as _orig
from django.http import HttpResponse
import json

def captcha_refresh(request):
    # 去掉 AJAX 限制,其余逻辑不变
    return _orig(request)

urls.py 里用 myapp.views.captcha_refresh 覆盖即可。


9. 结语:小功能,大体验

一次看似简单的“点击刷新”,背后却涉及路由匹配、视图鉴权、AJAX 标识与前端缓存等多个细节。掌握 django-simple-captcha 的刷新机制后,你可以:

  • 零刷新替换验证码
  • 轻松扩展短信/邮箱验证码混合模式
  • 为后续接入 Redis 缓存、滑块验证码打下坚实基础

把这篇文章收藏起来,下次再遇到 404,再也不用“整个页面 reload”了!


10. 附录:一键复制版前端组件

<script>
/* 通用验证码刷新器
 * 依赖:django-simple-captcha 0.5+
 */
function bindCaptchaRefresh(triggerId) {
  document.getElementById(triggerId).addEventListener('click', e => {
    e.preventDefault();
    fetch("/captcha/refresh/", {
      headers: {"X-Requested-With": "XMLHttpRequest"}
    })
    .then(r => r.json())
    .then(d => {
      document.querySelector('img.captcha').src = d.image_url;
      document.querySelector('input[name="captcha_0"]').value = d.key;
    });
  });
}
bindCaptchaRefresh('refresh-captcha');
</script>

Happy coding!

posted @ 2025-11-07 16:03  masx200  阅读(4)  评论(0)    收藏  举报