注册功能实现 -- 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'));
            }
        }
    });

    //----------------------------------------------//
上一篇下一篇

猜你喜欢

热点阅读