Django+React注册:特定验证错误无法在UI展示的解决方案
Django + React 注册功能字段级错误精准显示修复方案
一、后端(Django DRF + Simple JWT)修改
1. 修正用户序列化器(serializers.py)
添加phone字段,规范密码验证逻辑,确保唯一性验证错误返回对应字段:
from rest_framework import serializers from django.contrib.auth.models import User class RegisterSerializer(serializers.ModelSerializer): phone = serializers.CharField(required=True, max_length=15) password2 = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'}) class Meta: model = User fields = ('username', 'email', 'phone', 'password', 'password2') extra_kwargs = { 'password': {'write_only': True}, 'username': { 'validators': [serializers.UniqueValidator(queryset=User.objects.all(), message="用户名已存在")] }, 'email': { 'validators': [serializers.UniqueValidator(queryset=User.objects.all(), message="邮箱已存在")] } } def validate(self, attrs): if attrs['password'] != attrs['password2']: raise serializers.ValidationError({'password2': "两次输入的密码不匹配"}) return attrs def create(self, validated_data): validated_data.pop('password2') user = User.objects.create_user(**validated_data) # 若User模型扩展了phone字段,可取消下方注释保存手机号 # user.phone = validated_data['phone'] # user.save() return user
2. 修正注册视图(views.py)
移除统一error字段返回,直接使用DRF序列化器自带的字段级错误响应:
from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView from .serializers import RegisterSerializer class RegisterView(APIView): def post(self, request): serializer = RegisterSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response({"message": "注册成功"}, status=status.HTTP_201_CREATED) # 返回序列化器自动生成的字段级错误字典 return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
3. 确认路由配置(urls.py)
确保路由正确绑定注册视图:
from django.urls import path from .views import RegisterView urlpatterns = [ path('register/', RegisterView.as_view(), name='register'), # 其他路由... ]
二、前端(React Vite + Daisy UI + Tailwind)修改
1. 修正AuthContext中的registerUser函数
正确捕获后端字段级错误并返回给组件:
// AuthContext.jsx export const registerUser = async (userData) => { try { const response = await fetch('/api/register/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData), }); const data = await response.json(); if (!response.ok) { throw new Error(JSON.stringify(data)); } return data; } catch (err) { const errorData = JSON.parse(err.message); return { errors: errorData }; } };
2. 修正Register.jsx组件
统一确认密码字段名,添加字段级错误渲染逻辑:
import { useState } from 'react'; import { registerUser } from './AuthContext'; const Register = () => { const [formData, setFormData] = useState({ username: '', email: '', phone: '', password: '', password2: '', }); const [errors, setErrors] = useState({}); const handleChange = (e) => { setFormData(prev => ({ ...prev, [e.target.name]: e.target.value })); if (errors[e.target.name]) { setErrors(prev => ({ ...prev, [e.target.name]: '' })); } }; const handleSubmit = async (e) => { e.preventDefault(); setErrors({}); const result = await registerUser(formData); if (result.errors) { setErrors(result.errors); } else { alert('注册成功'); // 注册成功后可添加跳转逻辑 } }; return ( <div className="container mx-auto p-4"> <form onSubmit={handleSubmit} className="space-y-4"> {/* 用户名字段 */} <div> <label className="label">用户名</label> <input type="text" name="username" value={formData.username} onChange={handleChange} className={`input input-bordered w-full ${errors.username ? 'input-error' : ''}`} /> {errors.username && <p className="text-red-500 text-sm mt-1">{errors.username[0]}</p>} </div> {/* 邮箱字段 */} <div> <label className="label">邮箱</label> <input type="email" name="email" value={formData.email} onChange={handleChange} className={`input input-bordered w-full ${errors.email ? 'input-error' : ''}`} /> {errors.email && <p className="text-red-500 text-sm mt-1">{errors.email[0]}</p>} </div> {/* 手机号字段 */} <div> <label className="label">手机号</label> <input type="text" name="phone" value={formData.phone} onChange={handleChange} className={`input input-bordered w-full ${errors.phone ? 'input-error' : ''}`} /> {errors.phone && <p className="text-red-500 text-sm mt-1">{errors.phone[0]}</p>} </div> {/* 密码字段 */} <div> <label className="label">密码</label> <input type="password" name="password" value={formData.password} onChange={handleChange} className={`input input-bordered w-full ${errors.password ? 'input-error' : ''}`} /> {errors.password && <p className="text-red-500 text-sm mt-1">{errors.password[0]}</p>} </div> {/* 确认密码字段 */} <div> <label className="label">确认密码</label> <input type="password" name="password2" value={formData.password2} onChange={handleChange} className={`input input-bordered w-full ${errors.password2 ? 'input-error' : ''}`} /> {errors.password2 && <p className="text-red-500 text-sm mt-1">{errors.password2[0]}</p>} </div> <button type="submit" className="btn btn-primary w-full">注册</button> </form> </div> ); }; export default Register;
内容的提问来源于stack exchange,提问作者Zulqarnain Ansari
相关产品推荐
相关产品推荐

