06 user registration functions to achieve

analysis

  • Business processes
    • If the username is empty, has been registered
    • Determine the phone number is empty, has been registered
    • Determine whether the password is empty, in the correct format
    • Judgment and confirm the password is the same password
    • Determining whether the message authentication code is empty, whether the correct format, whether the message authentication code of the same real
    • Request methods: POST
    • url definition:/users/register/
    • Request Parameters: url path parameter
parameter Types of Whether the front must pass description
username String Yes The user enters a user name
password String Yes The password entered by the user
password_repeat String Yes Repeat password entered by the user
mobile String Yes User input phone number
sms_code String Yes Message authentication code entered by the user

Because it is post request, when initiating a request to the back-end, it needs to be accompanied csrf_token

The back-end code implementation

  • users/views.py
from django.shortcuts import render
from django.views import View #导入类视图
from utils.json_fun import to_json_data
from utils.res_code import Code,error_map
from apps.users.forms import RegisterForm
from apps.users.models import Users
from django.contrib.auth import login,logout
import json


#用到渲染模板和提交数据用类视图比较多

class RegisterView(View):
    """
    #一般会注释url
    /users/register
    """
    def get(self,request):
        return render(request,'users/register.html')


#需要的字段:用户名,密码,确认密码,手机号,短信验证码
#请求方式post
#提交: form表单,ajax
#获取,验证
#步骤:
# 1.获取参数
# 2.校验参数
# 3.保存参数到数据库
# 4.返回给前端
    #1.获取参数
    def post(self,request):
        json_data = request.body    #从前端收到的json类型数据
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])

        dict_data = json.loads(json_data.decode('utf8'))    #将json类型转成字典
        #2.校验参数,用form表单方式校验
        form = RegisterForm(data=dict_data)
        if form.is_valid():
            #3.保存数据到数据库
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            mobile = form.cleaned_data.get('mobile')

            user = Users.objects.create_user(username=username,password=password,mobile=mobile)
            login(request,user)
            #4.返回给前端
            return to_json_data(errmsg='恭喜您,注册成功')

        else:
            #定义一个错误信息列表
            err_msg_list = []
            for item in form.errors.get_json_data().values():
                err_msg_list.append(item[0].get('message'))

            err_msg_str = '/'.join(err_msg_list)    #拼接错误信息为一个字符串
            return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)
  • New forms.py file in the users directory, as follows:
from django import forms
from django_redis import get_redis_connection
import re
from apps.verifications.constants import SMS_CODE_NUMS
from apps.users.models import Users

class RegisterForm(forms.Form):
    """
    #1.单个字段格式验证
    # 2.手机号,格式,是否已注册
    # 3.密码是否一致
    # 4.短信验证码是否跟数据不一致
    """
    #1.单个字段格式验证
    username = forms.CharField(label='用户名',
                               max_length=20,
                               min_length=5,
                               error_messages={'min_length':'用户名长度要大于5',
                                               'max_length':'用户名长度要小于20',
                                               'required':'用户名不能为空'}
                               )
    password = forms.CharField(label='密码',
                               max_length=20,
                               min_length=6,
                               error_messages={'min_length':'密码长度要大于6',
                                               'max_length':'密码长度要小于20',
                                               'required':'密码不能为空'}
                               )
    password_repeat = forms.CharField(label='确认密码',
                                      max_length=20,
                                      min_length=6,
                                      error_messages={'min_length':'密码长度要大于6',
                                                      'max_length':'密码长度要小于20',
                                                      'required':'确认密码不能为空'}
                                      )
    mobile = forms.CharField(label='手机号',
                             max_length=11,
                             min_length=11,
                             error_messages={'min_length':'手机号长度有误',
                                             'max_length':'手机号长度有误',
                                             'required':'手机号不能为空'}
                             )
    sms_code = forms.CharField(label='短信验证码',
                               max_length=SMS_CODE_NUMS,
                               min_length=SMS_CODE_NUMS,
                               error_messages={'min_length':'短信验证码长度有误',
                                               'max_length':'短信验证码长度有误',
                                               'required':'短信验证码不能为空'}
                               )

    def clean_mobile(self):
        """
        # 2.手机号,格式,是否已注册
        :return:
        """
        tel = self.cleaned_data.get('mobile')   #从前端获取
        #验证输入的手机号格式
        if not re.match(r'^1[3-9]\d{9}$',tel):  #效果同verifications的forms.py中的RegexValidator
            raise forms.ValidationError('手机号码格式不正确')
        #验证输入的手机号在数据库中是否存在
        if Users.objects.filter(mobile=tel).exists():
            raise forms.ValidationError('手机号已注册!请重新输入!')

        return tel

    def clean(self):
        """
        # 3.密码是否一致
        # 4.短信验证码是否跟数据不一致
        :return:
        """
        #cleaned_data 就是读取前端form表单返回的值,返回类型为字典dict型
        cleaned_data = super().clean()  #继承clean的方法,重写
        # 3.密码是否一致
        password = cleaned_data.get('password')     #从那个字典中获取键password对应的值
        password_repeat = cleaned_data.get('password_repeat')
        if password != password_repeat: #判断密码和确认密码是否一致
            raise forms.ValidationError('两次输入密码不一致')

        sms_text = cleaned_data.get('sms_code')
        tel = cleaned_data.get('mobile')
        # 4.短信验证码是否跟数据不一致
        #连接redis数据库
        redis_conn = get_redis_connection('verity_codes')
        #构建短信验证码
        sms_fmt = 'sms_{}'.format(tel)
        #获取数据库中的短信验证码
        real_sms = redis_conn.get(sms_fmt)
        if (not real_sms) or (sms_text != real_sms.decode('utf-8')):    #判断结果为总是抛出异常
            raise forms.ValidationError('短信验证码错误')

Front-end code implementation

$(function () {
  let $username = $('#user_name');  // 选择id为user_name的网页元素,需要定义一个id为user_name
  let $img = $(".form-item .captcha-graph-img img");  // 获取图像标签
  let sImageCodeId = "";  // 定义图像验证码ID值
  let $mobile = $('#mobile');  // 选择id为mobile的网页元素,需要定义一个id为mobile
  let $smsCodeBtn = $('.form-item .sms-captcha');  // 获取短信验证码按钮元素,需要定义一个id为input_smscode
  let $imgCodeText = $('#input_captcha');  // 获取用户输入的图片验证码元素,需要定义一个id为input_captcha
  let $register = $('.form-contain');  // 获取注册表单元素


  // 1、图片验证码逻辑
  // 通过uuid生成验证码编号
  // 拼接验证码地址
  // 设置验证码图片标签的src
  generateImageCode();  // 生成图像验证码图片
  $img.click(generateImageCode);  // 点击图片验证码生成新的图片验证码图片


  // 2、用户名验证逻辑
  $username.blur(function () {
    fn_check_usrname();
  });


  // 3、手机号验证逻辑
  // 判断用户手机号是否注册
  $mobile.blur(function () {
    fn_check_mobile();
  });


  // 4、发送短信验证码逻辑
  $smsCodeBtn.click(function () {
    // 判断手机号是否输入
    if (fn_check_mobile() !== "success") {
      return
    }

    // 判断用户是否输入图片验证码
    let text = $imgCodeText.val();  // 获取用户输入的图片验证码文本
    if (!text) {
        message.showError('请填写验证码!');
        return
    }

    if (!sImageCodeId) {
      message.showError('图片UUID为空');
      return
    }

    // 正常
    let SdataParams = {
      "mobile": $mobile.val(),   // 获取用户输入的手机号
      "text": text,   // 获取用户输入的图片验证码文本
      "image_code_id": sImageCodeId  // 获取图片UUID
    };

    // for test
    // let SdataParams = {
    //   "mobile": "1806508",   // 获取用户输入的手机号
    //   "text": "ha3d",  // 获取用户输入的图片验证码文本
    //   "image_code_id": "680a5a66-d9e5-4c3c-b8ea"  // 获取图片UUID
    // };

    // 向后端发送请求
    $.ajax({
      // 请求地址
      url: "/sms_codes/",
      // 请求方式
      type: "POST",
      // 向后端发送csrf token
      // headers: {
      //           // 根据后端开启的CSRFProtect保护,cookie字段名固定为X-CSRFToken
      //           "X-CSRFToken": getCookie("csrf_token")
      // },
      // data: JSON.stringify(SdataParams),
      data: JSON.stringify(SdataParams),
      // 请求内容的数据类型(前端发给后端的格式)
      contentType: "application/json; charset=utf-8",
      // 响应数据的格式(后端返回给前端的格式)
      dataType: "json",
      async: false
    })
      .done(function (res) {
        if (res.errno === "0") {
          // 倒计时60秒,60秒后允许用户再次点击发送短信验证码的按钮
           message.showSuccess('短信验证码发送成功');
          let num = 60;
          // 设置一个计时器
          let t = setInterval(function () {
            if (num === 1) {
              // 如果计时器到最后, 清除计时器对象
              clearInterval(t);
              // 将点击获取验证码的按钮展示的文本恢复成原始文本
              $smsCodeBtn.html("获取验证码");
            } else {
              num -= 1;
              // 展示倒计时信息
              $smsCodeBtn.html(num + "秒");
            }
          }, 1000);
        } else {
          message.showError(res.errmsg);
        }
      })
      .fail(function(){
        message.showError('服务器超时,请重试!');
      });

  });


  // 5、注册逻辑
  $register.submit(function (e) {
    // 阻止默认提交操作
    e.preventDefault();

    // 获取用户输入的内容
    let sUsername = $username.val();  // 获取用户输入的用户名字符串
    let sPassword = $("input[name=password]").val();
    let sPasswordRepeat = $("input[name=password_repeat]").val();
    let sMobile = $mobile.val();  // 获取用户输入的手机号码字符串
    let sSmsCode = $("input[name=sms_captcha]").val();

    // 判断用户名是否已注册
    if (fn_check_usrname() !== "success") {
      return
    }

    // 判断手机号是否为空,是否已注册
    if (fn_check_mobile() !== "success") {
      return
    }

    // 判断用户输入的密码是否为空
    if ((!sPassword) || (!sPasswordRepeat)) {
      message.showError('密码或确认密码不能为空');
      return
    }

    // 判断用户输入的密码和确认密码长度是否为6-20位
    if ((sPassword.length < 6 || sPassword.length > 20) ||
      (sPasswordRepeat.length < 6 || sPasswordRepeat.length > 20)) {
      message.showError('密码和确认密码的长度需在6~20位以内');
      return
    }

    // 判断用户输入的密码和确认密码是否一致
    if (sPassword !== sPasswordRepeat) {
      message.showError('密码和确认密码不一致');
      return
    }


    // 判断用户输入的短信验证码是否为6位数字
    if (!(/^\d{6}$/).test(sSmsCode)) {
      message.showError('短信验证码格式不正确,必须为6位数字!');
      return
    }

    // 发起注册请求
    // 1、创建请求参数
    let SdataParams = { //后台接收的参数名字名字
      "username": sUsername,
      "password": sPassword,
      "password_repeat": sPasswordRepeat,
      "mobile": sMobile,
      "sms_code": sSmsCode
    };

    // 2、创建ajax请求,通过ajax返回给后台
    $.ajax({
      // 请求地址
      url: "/users/register/",  // url尾部需要添加/ url要和后台的一致
      // 请求方式
      type: "POST",
      data: JSON.stringify(SdataParams),
      // 请求内容的数据类型(前端发给后端的格式)
      contentType: "application/json; charset=utf-8",
      // 响应数据的格式(后端返回给前端的格式)
      dataType: "json",
    })
      .done(function (res) {
        if (res.errno === "0") {
          // 注册成功
          message.showSuccess('恭喜你,注册成功!');
          setTimeout(function () {
            // 注册成功之后重定向到主页
            window.location.href = document.referrer;
          }, 1000)
        } else {
          // 注册失败,打印错误信息
          message.showError(res.errmsg);
        }
      })
      .fail(function(){
        message.showError('服务器超时,请重试!');
      });

  });


  // 生成一个图片验证码的编号,并设置页面中图片验证码img标签的src属性
  function generateImageCode() {
    // 1、生成一个图片验证码随机编号
    sImageCodeId = generateUUID();
    // 2、拼接请求url /image_codes/<uuid:image_code_id>/
    let imageCodeUrl = "/image_codes/" + sImageCodeId + "/";
    // 3、修改验证码图片src地址
    $img.attr('src', imageCodeUrl)

  }

  // 生成图片UUID验证码
  function generateUUID() {
    let d = new Date().getTime();
    if (window.performance && typeof window.performance.now === "function") {
      d += performance.now(); //use high-precision timer if available
    }
    let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
      let r = (d + Math.random() * 16) % 16 | 0;
      d = Math.floor(d / 16);
      return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
    return uuid;
  }

  // 判断用户名是否已经注册
  function fn_check_usrname() {
    let sUsername = $username.val();  // 获取用户名字符串
    let sReturnValue = "";

    if (sUsername === "") {
      message.showError('用户名不能为空!');
      return
    }

    if (!(/^\w{5,20}$/).test(sUsername)) {
      message.showError('请输入5-20个字符的用户名');
      return
    }

    // 发送ajax请求,去后端查询用户名是否存在
    $.ajax({
      url: '/usernames/' + sUsername + '/',
      type: 'GET',
      dataType: 'json',
      async: false
    })
      .done(function (res) {
        if (res.data.count !== 0) {
          message.showError(res.data.username + '已注册,请重新输入!');
          sReturnValue = ""
        } else {
          message.showInfo(res.data.username + '能正常使用!');
          sReturnValue = "success"
        }
      })
      .fail(function () {
        message.showError('服务器超时,请重试!');
        sReturnValue = ""
      });
    return sReturnValue
  }

  // 判断手机号是否注册
  function fn_check_mobile() {
    let sMobile = $mobile.val();  // 获取用户输入的手机号码字符串
    let sReturnValue = "";
    if (sMobile === "") {
      message.showError('手机号不能为空!');
      return
    }
    if (!(/^1[345789]\d{9}$/).test(sMobile)) {
      message.showError('手机号码格式不正确,请重新输入!');
      return
    }

    $.ajax({
      url: '/mobiles/' + sMobile + '/',
      type: 'GET',
      dataType: 'json',
      async: false
    })
      .done(function (res) {
        if (res.data.count !== 0) {
          message.showError(res.data.mobile + '已注册,请重新输入!');
          sReturnValue = ""
        } else {
          message.showSuccess(res.data.mobile + '能正常使用!');
          sReturnValue = "success"
        }
      })
      .fail(function () {
        message.showError('服务器超时,请重试!');
        sReturnValue = ""
      });
    return sReturnValue

  }

  // 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'));
      }
    }
  });

});

Guess you like

Origin blog.csdn.net/xiaogeldx/article/details/90680411