Django注册视图无法添加新用户?403禁止访问问题排查
DRF APIView实现OTP注册时出现403 Forbidden错误
我用DRF的APIView搭建了带OTP的基础用户注册功能,调用http://127.0.0.1:8000/auth/register时,控制台显示Forbidden: /auth/register和403状态码,页面提示“Error creating account: undefined”,无法创建新用户。
注册视图代码
class RegisterView(APIView): permission_classes = [AllowAny] def get(self, request, *args, **kwargs): return render(request, 'registration.html') def post(self, request, *args, **kwargs): serializer = RegisterSerializer(data=request.data) if serializer.is_valid(): user = serializer.save() # 发送带OTP的验证邮件 otp = generate_otp() user.otp = otp user.otp_expiry = datetime.now() + timedelta(minutes=5) user.save() send_verification_email(user.email, otp) return Response( { 'message': 'Account created successfully. Please verify your email.', "status": status.HTTP_201_CREATED, } ) return Response( { 'message': serializer.errors, "status": status.HTTP_400_BAD_REQUEST, } )
辅助函数代码
def generate_otp(): return str(random.randint(100000, 999999)) def send_verification_email(email, otp): subject = "Verify Your Email Address" template_name = "email_verification.html" context = {"otp": otp} html_content = render_to_string(template_name, context) text_content = strip_tags(html_content) msg = EmailMessage(subject, text_content, to=[email]) msg.content_subtype = "html" msg.send()
前端register.html代码
<form id="register-form"> {% csrf_token %} <div class="form-group"> <label for="email">Email Address:</label> <input type="email" class="form-control" id="email" name="email" required> </div> <div class="form-group"> <label for="password">Password:</label> <input type="password" class="form-control" id="password" name="password" required> </div> <button type="submit">Register</button> </form> <script> document.getElementById('register-form').addEventListener('submit', function(event) { event.preventDefault(); const formData = new FormData(event.target); const jsonData = {}; for (const [key, value] of formData.entries()) { jsonData[key] = value; } fetch('{% url "register" %}', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(jsonData) }) .then(response => response.json()) .then(data => { if (data.status === 201) { alert('Account created successfully. Please verify your email.'); } else { alert('Error creating account: ' + data.message); } }) .catch(error => { alert('Error creating account: ' + error.message); }); }); </script>
URL配置
应用urls.py
urlpatterns = [ path('register', RegisterView.as_view(), name='register'), ]
项目urls.py
path('auth/', include('user_authentication.urls'))
错误信息
- 控制台报错:
Forbidden: /auth/register [04/Jun/2024 16:38:42] "POST /auth/register HTTP/1.1" 403 45
- 页面提示:
Error creating account: undefined
原因分析
403错误的核心原因是Django的CSRF验证失败。前端通过fetch发送JSON请求时,虽然表单包含{% csrf_token %},但未将CSRF令牌正确携带到请求头中,Django默认拦截未携带CSRF令牌的POST请求,返回403。
页面提示“undefined”是因为403响应未返回JSON格式数据,导致response.json()解析失败进入catch块,而error.message为空或未定义。
解决方案
方案1:在fetch请求中手动携带CSRF令牌
修改前端JS代码,获取页面中的CSRF令牌并添加到请求头:
document.getElementById('register-form').addEventListener('submit', function(event) { event.preventDefault(); const formData = new FormData(event.target); const jsonData = {}; for (const [key, value] of formData.entries()) { jsonData[key] = value; } // 获取页面中的CSRF令牌 const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value; fetch('{% url "register" %}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken // 添加CSRF令牌请求头 }, body: JSON.stringify(jsonData) }) .then(response => { // 先检查响应状态,再处理JSON解析 if (!response.ok) { return response.json().then(err => Promise.reject(err)); } return response.json(); }) .then(data => { if (data.status === 201) { alert('Account created successfully. Please verify your email.'); } else { alert('Error creating account: ' + data.message); } }) .catch(error => { const errorMsg = error.message || '权限验证失败(CSRF令牌错误)'; alert('Error creating account: ' + errorMsg); }); });
方案2:改用FormData发送请求(自动携带CSRF)
如果不需要JSON格式,直接发送FormData,Django会自动处理CSRF令牌:
document.getElementById('register-form').addEventListener('submit', function(event) { event.preventDefault(); const formData = new FormData(event.target); fetch('{% url "register" %}', { method: 'POST', body: formData // 直接发送FormData,无需手动设置Content-Type }) .then(response => { if (!response.ok) { return response.json().then(err => Promise.reject(err)); } return response.json(); }) .then(data => { if (data.status === 201) { alert('Account created successfully. Please verify your email.'); } else { alert('Error creating account: ' + data.message); } }) .catch(error => { const errorMsg = error.message || '请求失败'; alert('Error creating account: ' + errorMsg); }); });
方案3:视图中禁用CSRF验证(不推荐)
仅适用于纯无状态API场景,不适合前后端同构项目:
from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator @method_decorator(csrf_exempt, name='dispatch') class RegisterView(APIView): permission_classes = [AllowAny] # 其余代码不变
内容的提问来源于stack exchange,提问作者user3521180
相关产品推荐
相关产品推荐

