You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 21:35:06