Django DRF服务端校验reCAPTCHA响应 验证通过才允许数据入库
问题根因
你现在的代码有几个硬伤,直接导致校验完全失效:
- 请求取值逻辑错了:前端发的是
application/json格式的请求,request.POST只接收form-urlencoded、form-data类型的请求体,你写的request.POST.get('g-recaptcha-response')永远拿不到值,手写的那堆校验逻辑从根上就取不到验证码token,等于白写 - 配置名写错:settings里给drf-recaptcha配的密钥变量是
DRF_RECAPTCHA_SECRET_KEY,手写校验里取的是GOOGLE_RECAPTCHA_SECRET_KEY,俩名字对不上,就算拿到token也会因为密钥不对校验失败 - 重复造轮子还漏了拦截分支:你已经在序列化器里加了
ReCaptchaField,根本不需要自己手写代码调用谷歌校验接口,而且你手写的逻辑只处理了校验成功的分支,校验失败的时候没有任何拦截返回,逻辑有缺口 - 字段名对不上:序列化器里验证码字段叫
recaptcha,你手动取的参数名是g-recaptcha-response,前端如果没把token传到recaptcha字段里,序列化器自带的校验也触发不了 - 代码版本不兼容:视图里用的
urllib/urllib2是Python2的写法,Python3环境下跑这段直接报错,校验逻辑根本执行不下去
正确实现方案
直接用drf-recaptcha自带的校验能力即可,不需要自己手写请求调用校验接口,改四个地方就行:
1. 修正settings.py配置
确保配置的密钥名和drf-recaptcha要求的一致,不要自定义变量名:
INSTALLED_APPS = [ # 你的其他业务应用 'rest_framework', 'rest_framework_recaptcha', ] # 变量名必须是DRF_RECAPTCHA_SECRET_KEY,不要随意修改 DRF_RECAPTCHA_SECRET_KEY = '<你的reCAPTCHA服务端密钥>'
2. 修正序列化器
如果你的Contact数据表本身没有recaptcha字段,要加write_only=True参数,避免序列化器保存时报字段不存在的错误,验证码字段只做写入校验,不需要存入数据库:
from rest_framework import serializers from rest_framework_recaptcha import ReCaptchaField from .models import Contact class ContactSerializer(serializers.ModelSerializer): recaptcha = ReCaptchaField(write_only=True) class Meta: model = Contact fields = '__all__'
3. 简化视图逻辑,删除冗余手写校验代码
drf-recaptcha的字段会在serializer.is_valid()阶段自动完成验证码校验,校验不通过会直接返回错误,根本不会走到数据保存逻辑:
from rest_framework.decorators import api_view from rest_framework.response import Response from .serializers import ContactSerializer @api_view(['POST', 'GET']) def contact_forms_api(request): if request.method == 'POST': serializer = ContactSerializer(data=request.data) # is_valid()会自动校验recaptcha字段,验证码无效、过期、为空都会直接抛错,不会执行save if serializer.is_valid(raise_exception=True): serializer.save() return Response({ 'status': True, 'message': '提交成功' }) # GET请求逻辑自行补充,比如返回历史提交记录 return Response({ 'status': False, 'message': '请求方式不合法' })
4. 修正前端提交逻辑
确保把react-google-recaptcha拿到的token,放在请求体的recaptcha字段里,和后端序列化器字段名完全对应:
import { useState } from 'react' import ReCAPTCHA from 'react-google-recaptcha' import axios from 'axios' export default function ContactForm() { // 存储验证码token const [recaptchaToken, setRecaptchaToken] = useState('') // 验证码加载完成的回调 const handleRecaptcha = (token) => { setRecaptchaToken(token) } // 表单提交逻辑 const SubmitContact = async (e) => { e.preventDefault() // 组装表单数据,替换成你实际的表单字段 const formData = { name: e.target.name.value, contact: e.target.contact.value, content: e.target.content.value, recaptcha: recaptchaToken // 字段名必须为recaptcha,和后端对齐 } // 发送请求 await axios.post('/api/contact', formData, { headers: { 'Content-Type': 'application/json' } }) } return ( <form onSubmit={SubmitContact}> <input type="text" name="name" placeholder="姓名" /> <input type="text" name="contact" placeholder="联系方式" /> <textarea name="content" placeholder="留言内容"></textarea> <ReCAPTCHA sitekey="<你的前端reCAPTCHA sitekey>" onChange={handleRecaptcha} /> <button type="submit">提交</button> </form> ) }
校验逻辑说明
改完之后逻辑是完全闭环的:
- 用户没勾选验证码,
recaptchaToken为空,后端序列化器校验直接返回recaptcha字段不能为空的错误,不会执行入库操作 - 用户传的验证码token无效、过期,drf-recaptcha会自动调用reCAPTCHA服务端接口校验,校验失败直接返回错误,不会入库
- 只有验证码校验通过、其他表单字段也符合规则的情况下,才会执行
serializer.save()把数据写入数据库
注意:如果服务部署在国内,要确保你的服务器能正常访问reCAPTCHA的校验接口,不然会出现接口超时、校验失败的问题。
内容的提问来源于stack exchange,提问作者boyenec
相关产品推荐
相关产品推荐

