二十. 用户注册 — 实现注册功能
注:该篇文章接上一篇 十九 .用户注册 — 短信验证码实现
在上一篇文章我们实现了用户注册中的短信验证码功能,在这一章实现注册功能
实现注册要完成的图表
实现注册模块的整体流程
根据流程图总结注册业务包含如下功能
- 注册页面
- 图片验证码
- 用户名检测是否注册
- 手机号检测是否注册
- 短信验证码
- 注册保存用户数据
九、注册功能
1.业务流程分析
- 判断用户名是否为空,是否已注册
- 判断密码是否为空,格式是否正确
- 判断两次密码是否一致
- 判断手机号码是否为空,格式是否正确
- 判断短信验证码是否为空,格式是否正确,是否与真实短信验证码相同
2.接口设计
接口说明:
类目 | 说明 |
---|---|
请求方法 | POST |
url定义 | /user/register/ |
参数格式 | 表单 |
注意:post请求,前端请求要带上csrf token
参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
username | 字符串 | 是 | 用户输入的用户名 |
password | 字符串 | 是 | 用户输入的密码 |
password_repeat | 字符串 | 是 | 用户输入的重复密码 |
mobile | 字符串 | 是 | 用户输入的手机号码 |
sms_code | 字符串 | 是 | 用户输入的短信验证码 |
返回结果:
{
"errno": "0",
"errmsg": "恭喜您,注册成功!",
}
3.后端代码
- user/views.py
from django.shortcuts import render
from django.views import View
from .forms import RegisterForm
from .models import User
from utils.json_res import json_response
from utils.res_code import Code,error_map
# Create your views here.
def login(request):
return render(request, 'user/login.html')
# def register(request):
# return render(request,'user/register.html')
class RegisterView(View):
def get(self, request):
return render(request, 'user/register.html')
def post(self,requset):
# 写一个表单,用于post接口,用于验证填写的信息
form = RegisterForm(requset.POST)
if form.is_valid():
#验证form表单
#获取表单信息
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
mobile = form.cleaned_data.get('mobile')
#存储到用户模型中
User.objects.create_user(username = username,password = password,mobile = mobile)
return json_response(errmsg='恭喜你注册成功')
else:
#定义错误表信息
# 将表单的报错信息进行拼接
err_msg_list = []
for item in form.errors.get_json_data().values():
err_msg_list.append(item[0].get('message'))
# print(item[0].get('message')) # for test
err_msg_str = '/'.join(err_msg_list) # 拼接错误信息为一个字符串
return json_response(errno=Code.PARAMERR, errmsg=err_msg_str)
- user/forms.py代码:
import re # 用于验证信息格式
from django_redis import get_redis_connection # 连接redis库
from .models import User # 导入用户库模型
from django import forms
from verification.constants import SMS_CODE_LENGTH
class RegisterForm(forms.Form):
username = forms.CharField(label='用户名', max_length=20, min_length=5,
error_messages={
'max_length': '用户名长度要小于20',
'min_length': '用户名长度要大于4',
'required': '用户名不能为空'
})
password = forms.CharField(label='密码', max_length=20, min_length=6,
error_messages={
'max_length': '密码长度要小于20',
'min_length': '密码长度要大于5',
'required': '用户名不能为空'
})
password_repeat = forms.CharField(label='确认密码', max_length=20, min_length=6,
error_messages={
'max_length': '密码长度要小于20',
'min_length': '密码长度要大于5',
'required': '用户名不能为空'
})
mobile = forms.CharField(label='手机号码', max_length=11, min_length=11,
error_messages={
'max_length': '手机号码长度有误',
'min_length': '手机号码长度有误',
'required': '手机号码不能为空'
})
sms_code = forms.CharField(label='短信验证码', max_length=SMS_CODE_LENGTH, min_length=SMS_CODE_LENGTH,
error_messages={
'max_length': '短信验证码长度有误',
'min_length': '短信验证码长度有误长度有误',
'required': '短信验证码不能为空'
})
def clean_username(self):
"""
校验用户名
:return:
"""
username = self.cleaned_data.get('username')
if User.objects.filter(username=username).exists():
return forms.ValidationError('用户名已存在!')
return username
def clean_mobile(self):
"""
校验手机号
:return:
"""
mobile = self.cleaned_data.get('mobile')
if not re.match(r'^1[3-9]\d{9}$', mobile):
raise forms.ValidationError('手机号码格式不正确')
if User.objects.filter(mobile=mobile).exists():
raise forms.ValidationError('手机号码已注册!')
return mobile
def clean(self):
"""
校验,密码,和短信验证码
:return:
"""
clean_data = super().clean()
# 校验密码是否一致
password = clean_data.get('password')
password_repeat = clean_data.get('password_repeat')
if password != password_repeat:
raise forms.ValidationError('两次密码不一致!')
# 校验短信验证码
sms_code = clean_data.get('sms_code')
moblie = clean_data.get('mobile')
# 获取rides中缓存的验证码,get_redis_connection() -- 指定与哪个数据库建立连接
redis_conn = get_redis_connection(alias='verify_code')
real_code = redis_conn.get('sms_text_{}'.format(moblie))
if (not real_code) or (real_code.decode('utf-8') != sms_code):
raise forms.ValidationError('短信验证码错误!')
4.前端js代码
$(function () {
// 定义状态变量
//判断状态是false还是ture,如果是ture就直接引用
let isUsernameReady = false,
isPasswordReady = false,
isMobileReady = false,
isSmsCodeReady = false;
let $img = $('.form-contain .form-item .captcha-graph-img img');
// 1.点击刷新图像验证码
$img.click(function () {
$img.attr('src', '/image_code/?rand=' + Math.random())
});
// 2.鼠标离开用户名输入框校验用户名
let $username = $('#username'); //获取前端页面中的username框
$username.blur(fnCheckUsername); //blur是鼠标离开事件
//鼠标离开username触发函数fnCheckUsername
function fnCheckUsername() {
isUsernameReady = false; //校验用户名,没填入用户名为false
let sUsername = $username.val(); // 获取用户名字符串
if (sUsername === '') {
// 如果用户名为空,返回消息
message.showError('用户名不能为空')
return
}
if (!(/^\w{5,20}$/).test(sUsername)) {
//检测用户名长度
message.showError('请输入5-20个字符的用户名');
return
}
$.ajax({
url: '/username/' + sUsername + '/', //发送请求的地址
type: 'GET', //请求方式
dataType: 'json', //预期服务器返回的数据类型
success: function (data) {
// data 由服务器返回的数据,我们这里是json类型
if (data.data.data.count !== 0) {
//根据json的数据结构的到用户数量
message.showError(data.data.data.username + '已经注册,请重新输入!')
} else {
message.showInfo(data.data.username + '可以正常使用!');
isUsernameReady = true
}
},
error: function (xhr, msg) {
//请求失败时被调用的函数
message.showError('服务器超时,请重试!')
}
});
}
// 3.检测密码是否一致
let $passwordRepeat = $('input[name="password_repeat"]'); //获取前端input[name="password_repeat"] 中的内容(二次密码)
$passwordRepeat.blur(fnCheckPassword); //blur是鼠标离开事件
//鼠标离开username触发函数fnCheckPassword
function fnCheckPassword() {
isPasswordReady = false; //校验密码,没填入密码为false
let password = $('input[name="password"]').val(); // 获得密码字符串
let passwordRepeat = $passwordRepeat.val(); // 获取用户二次密码字符串
if (password === '' || passwordRepeat === '') {
message.showError('密码不能为空');
return
}
if (password !== passwordRepeat) {
message.showError('两次密码输入不一致');
return
}
if (password === passwordRepeat) {
isPasswordReady = true
}
}
// 4.检查手机号码是否可用
let $mobile = $('input[name="mobile"]'); //获取前端input[name="mobile"] 中的内容(手机密码)
$mobile.blur(fnCheckMobile);
//鼠标离开username触发函数fnCheckMobile
function fnCheckMobile () {
isMobileReady = true;
let sMobile = $mobile.val(); // 获得手机字符串
if(sMobile === ''){
message.showError('手机号码不能为空');
return
}
if(!(/^1[3-9]\d{9}$/).test(sMobile)){
message.showError('手机号码格式不正确');
return
}
$.ajax({
url: '/mobile/' + sMobile + '/',
type: 'GET',
dataType: 'json',
success: function (data) {
if(data.data.count !== 0){
message.showError(data.data.mobile + '已经注册,请重新输入!')
}else {
message.showInfo(data.data.mobile + '可以正常使用!');
isMobileReady = true
}
},
error: function (xhr, msg) {
message.showError('服务器超时,请重试!')
}
});
}
// 5.发送手机验证码
let $smsButton = $('.sms-captcha');
$smsButton.click(function (){
let sCaptcha = $('input[name="captcha_graph"]').val();
if (sCaptcha === '') {
message.showError('请输入验证码');
return
}
if (!isMobileReady) {
fnCheckMobile();
return
}
$.ajax({
url: '/sms_code/',
type: 'POST',
data: {
mobile: $mobile.val(),
captcha: sCaptcha
},
dataType: 'json',
success: function (data) {
if (data.errno !== '0') {
message.showError(data.errmsg)
} else {
message.showSuccess(data.errmsg);
let num = 60;
//设置计时器
let t = setInterval(function () {
if (num === 1) {
clearInterval(t)
}
})
}
},
error: function (xhr, msg) {
message.showError('服务器超时,请重试!')
}
});
});
// 6.注册
let $submitBtn = $('.register-btn');
$submitBtn.click(function (e) {
//阻止默认提交
e.preventDefault();
// 1.检查用户名
if(!isUsernameReady){
fnCheckUsername();
return
}
// 2.检查密码
if(!isPasswordReady){
fnCheckPassword();
return
}
// 3.检查电话号码
if(!isMobileReady){
fnCheckMobile();
return
}
// 4.检查短信验证码
let sSmsCode = $('input[name="sms_captcha"]').val();//获取前端input[sms_captcha] 中的内容(短信验证码)
if(sSmsCode === ''){
message.showError('短信验证码不能为空!');
return
}
if(!(/^\d{4}$/).test(sSmsCode)){
message.showError('短信验证码长度不正确,必须是4位数字!');
return
}
$.ajax({
url: '/register/',
type: 'POST',
data:{
username: $username.val(),
password: $('input[name="password"]').val(),
password_repeat: $passwordRepeat.val(),
mobile: $mobile.val(),
sms_code: sSmsCode
},
dataType: 'json',
success: function (res) {
if(res.errno === '0'){
message.showSuccess('恭喜您,注册成功!');
setTimeout(function () {
//注册成功后重定向到登录页面
window.location.href = '/login/'
}, 3000)
}else{
//注册失败
message.showError(res.errmsg)
}
},
error: function () {
message.showError('服务器超时,请重试!')
}
})
});
});