注册功能实现 -- 7.用户注册功能实现
2019-03-01 本文已影响0人
爱修仙的道友
七、用户注册功能实现
1.分析
业务处理流程:
- 判断用户名是否为空,是否已注册
- 判断手机号是否为空,是否已注册
- 判断密码是否为空,格式是否正确
- 判断确认密码与密码是否相同
- 判断短信验证码是否为空,是否格式正确,是否与真实的短信验证码相同
请求方法:POST
url定义:/users/register/
请求参数:请求体传参
参数 | 类型 | 前端是否必须传 | 描述 |
---|---|---|---|
username | 字符串 | 是 | 用户输入的用户名 |
password | 字符串 | 是 | 用户输入的密码 |
password_repeat | 字符串 | 是 | 用户输入的重复密码 |
mobile | 字符串 | 是 | 用户输入的手机号 |
user_sms_text | 字符串 | 是 | 用户输入的短信验证码 |
注:由于是post请求,在向后端发起请求时,需要附带csrf token
2.后端代码实现
# views.py
from django.views import View
from django.shortcuts import render
from django.contrib.auth import login
from .models import Users
from .forms import RegisterForms
from utils.res_code.rescode import Code, error_map
from utils.json_translate.json_fun import to_json_data
logger = logging.getLogger('django')
# 用户注册逻辑实现
class RegisterView(View):
"""
用户注册逻辑视图
-- get
-- post
"""
def get(self, request):
"""
渲染登陆页面
:param request:
:return:
"""
return render(request, 'users/register.html')
def post(self, request):
"""
1.创建一个类视图 RegisterView
2.明确请求方式:
-- 请求类型 Ajax post
-- 传参方式及参数 请求体 username password password_repeat mobile user_sms_text
-- url路径 '/users/register/'
3.获取前端数据
-- json_data = request.body
4.业务处理
-- 是否需要校验 需要,form表单校验
-- 是否需要储存 需要,mysql数据库存储用户信息
-- 其他:
-- 日志器展示用户信息
-- 返回用户登陆界面
5.返回前端数据:
-- to_json_data(errno= ,errmsg=, data=)
:param request:
:return:
"""
# 获取前端参数
json_data = request.body
# 若无参数
if not json_data:
return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
# 将字节类型的json参数转化为dict
dict_data = json.loads(json_data.decode('utf-8'))
# form表单校验
form = RegisterForms(dict_data)
if form.is_valid():
# 获取参数
cleaned_mobile = form.cleaned_data.get('mobile')
cleaned_password = form.cleaned_data.get('password')
cleaned_username = form.cleaned_data.get('username')
# 保存数据 mysql
try:
user = Users.objects.create_user(username=cleaned_username, password=cleaned_password, mobile=cleaned_mobile)
except Exception as e:
return to_json_data(errno=Code.SERVERERR, errmsg=error_map[Code.SERVERERR])
# 自动处理session信息,保持登陆
login(request, user)
logger.info('恭喜{}用户注册成功'.format(user))
return to_json_data(errno=Code.OK, errmsg='恭喜你注册成功')
else:
# 定义一个接收错误信息的列表
form_errormsg_list = []
for msg in form.errors.get_json_data().values():
form_errormsg_list.append(msg[0].get('message'))
form_errormsg = '/'.join(form_errormsg_list)
return to_json_data(errno=Code.PARAMERR, errmsg=form_errormsg)
# forms.py
import re
from django import forms
from django_redis import get_redis_connection
from django.core.validators import RegexValidator
from .models import Users
from verifications.constants import SMS_CODE_NUMS
password_Validator = RegexValidator(r'^\w{6,20}$', '密码格式不正确,请重新输入')
# 注册表单校验
class RegisterForms(forms.Form):
"""
获取参数 --
username password password_repeat mobile user_sms_text
业务逻辑 --
判断用户名是否符合标准(是否注册,是否为空,是否合法)
判断手机号是否符合标准(是否注册,是否为空,是否合法)
判断密码是否符合标准(是否为空,格式是否正确)
判断确认密码是否符合标准(是否为空,格式是否正确,是否与密码相同)
判断短信密码是否符合标准(是否为空,格式是否正确,是否与redis保存的相同)
"""
username = forms.CharField(max_length=20, min_length=5, label='用户名',
error_messages={
'max_length':'用户名长度要小于20字符',
'min_length':'用户名长度要大于5字符',
'required':'用户名不能为空'
})
password = forms.CharField(max_length=20, min_length=6, label='密码', validators=[password_Validator,],
error_messages={
'max_length':'密码长度要小于20位',
'min_length':'密码长度要大于6位',
'required':'密码不能为空',
})
password_repeat = forms.CharField(max_length=20, min_length=6, label='密码', validators=[password_Validator,],
error_messages={
'max_length':'密码长度要小于20位',
'min_length':'密码长度要大于6位',
'required':'密码不能为空',
})
mobile = forms.CharField(max_length=11, min_length=11, label='手机号',
error_messages={
'max_length':'手机号长度要为11位',
'min_length':'手机号长度要为11位',
'required':'手机号不能为空',
})
user_sms_text = forms.CharField(max_length=SMS_CODE_NUMS, min_length=SMS_CODE_NUMS, label='短信验证码',
error_messages={
'max_length':'短信验证码输入有误',
'min_length':'短信验证码输入有误',
'required':'短信验证码不能为空'
})
def clean_username(self):
cleaned_username = self.cleaned_data.get('username')
if Users.objects.filter(username=cleaned_username).count():
raise forms.ValidationError('该用户名已经注册')
return cleaned_username
def clean_mobile(self):
cleaned_mobile = self.cleaned_data.get('mobile')
if not re.match(r'^1[3-9]\d{9}$', cleaned_mobile):
raise forms.ValidationError('输入手机号格式不正确')
if Users.objects.filter(mobile=cleaned_mobile).count():
raise forms.ValidationError('该手机号已经注册')
return cleaned_mobile
def clean(self):
"""
password password_repeat
user_sms_text redis_sms_text
:return:
"""
cleaned_data = super().clean()
cleaned_password = cleaned_data.get('password')
cleaned_password_repeat = cleaned_data.get('password_repeat')
if cleaned_password != cleaned_password_repeat:
raise forms.ValidationError('两次密码输入不一致')
cleaned_mobile = cleaned_data.get('mobile')
cleaned_user_sms_text = cleaned_data.get('user_sms_text')
# 读取短信验证码(redis)
try:
# redis 读取数据操作三部曲
# 1.连接redis
conn_redis = get_redis_connection(alias='verify_codes')
# 2.建立key值 -- 二进制形式
sms_text_key = 'sms_text_{}'.format(cleaned_mobile).encode('utf-8')
# 3.读取数据
redis_sms_content = conn_redis.get(sms_text_key)
except Exception as e:
raise forms.ValidationError('数据库连接错误')
if not redis_sms_content or (redis_sms_content.decode('utf-8') != cleaned_user_sms_text):
raise forms.ValidationError('验证码输入错误')
# urls.py
from django.urls import path
from . import views
app_name = 'users'
urlpatterns = [
path('login/', views.LoginView.as_view(), name='user_login'),
path('register/', views.RegisterView.as_view(), name='user_register'),
]
3.前端代码实现
# register.py
//---------------------注册逻辑-------------------------//
let $register = $('.form-contain'); // 获取注册表单元素
$register.submit(function (e) {
// 阻止默认提交
e.preventDefault();
// 获取用户输入内容:
let sUsername = $username.val();
let sPassword = $('input[name=password]').val();
let sPassword_Repeat = $('input[name=password_repeat]').val();
let sMobile = $mobile.val();
let sSmsCode = $('input[name=sms_captcha]').val();
//判断用户名是否已注册
let susername = fn_check_username(); //不加async: false,永远返回define/""
if (susername===''){
return
}
// 判断手机号是否为空,是否已注册
let smobile = fn_check_mobile();
if (smobile===''){
return
}
// 判断用户输入的密码是否为空
if ((!sPassword) || (!sPassword_Repeat)) {
message.showError('密码或确认密码不能为空');
return
}
// 判断用户输入的密码和确认密码长度是否符合标准
if (!(/^\w{6,20}$/).test(sPassword)) {
message.showError('密码格式:匹配包括下划线的任何英文单词字符');
return
}
if (!(/^\w{6,20}$/).test(sPassword_Repeat)) {
message.showError('密码格式:匹配包括下划线的任何英文单词字符');
return
}
// 判断用户输入的密码和确认密码是否一致
if (sPassword !== sPassword_Repeat) {
message.showError('密码和确认密码不一致');
return
}
// 判断用户输入的短信验证码是否为6位数字
if (!(/^\d{6}$/).test(sSmsCode)) {
message.showError('短信验证码格式不正确,必须为6位数字!');
return
}
// 发起注册请求
// 1、创建请求参数
let SdataParams = {
"username": sUsername,
"password": sPassword,
"password_repeat": sPassword_Repeat,
"mobile": sMobile,
"user_sms_text": sSmsCode
};
// 2、创建ajax请求
$.ajax({
// 请求地址
url: "/users/register/", // url尾部需要添加/
// 请求方式
type: "POST",
data: JSON.stringify(SdataParams),
// 请求内容的数据类型(前端发给后端的格式)
contentType: "application/json; charset=utf-8",
// 响应数据的格式(后端返回给前端的格式)
dataType: "json"
})
.done(function (res) {
if (res.errno === "200") {
// 注册成功
message.showSuccess('恭喜你,注册成功!');
setTimeout(function () {
// 注册成功之后重定向到主页
window.location.href = '/users/login/';
}, 1000)
} else {
// 注册失败,打印错误信息
message.showError(res.errmsg);
}
})
.fail(function () {
message.showError('服务器超时,请重试!');
});
});
//----------------------------------------------//
// 这三步 可以将ajax的post请求添加 csrf
// get cookie using jQuery
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
let cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// Setting the token on the AJAX request
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
//----------------------------------------------//