## 用戶注冊功能
[TOC]
### 一、設(shè)計接口思路
-?分析業(yè)務邏輯汁咏,明確在這個業(yè)務中需要涉及到幾個相關(guān)子業(yè)務隙赁,將每個子業(yè)務當做一個接口來設(shè)計
-?分析接口的功能任務,明確接口的訪問方式與返回數(shù)據(jù):
??-?接口的請求方式梆暖,如GET 伞访、POST 、PUT等
??-?接口的URL路徑定義
??-?需要前端傳遞的數(shù)據(jù)及數(shù)據(jù)格式(如路徑參數(shù)轰驳、查詢字符串厚掷、請求體表單弟灼、JSON等)
??-?返回給前端的數(shù)據(jù)及數(shù)據(jù)格式
### 二、功能分析
-?用戶名判斷是否存在
-?手機號判斷是否存在
-?圖片驗證碼
-?短信驗證碼
-?注冊保存用戶數(shù)據(jù)
圖片驗證碼冒黑、短信驗證碼考慮到后續(xù)可能會在其他業(yè)務中也會用到田绑,因此將驗證碼功能獨立出來,**創(chuàng)建一個新應用verifications抡爹,在此應用中實現(xiàn)圖片驗證碼掩驱、短信驗證碼**
### 三、圖片驗證碼接口代碼實現(xiàn)
#### 1.分析
**請求方法**:**GET**
**url定義**:`/image_codes/<uuid:image_code_id>/`
**請求參數(shù)**:url路徑參數(shù)
| 參數(shù)??????????| 類型???????| 前端是否必須傳 | 描述???????????|
| ------------- | ---------- | -------------- | -------------- |
| image_code_id | uuid字符串 | 是?????????????| 圖片驗證碼編號 |
uuid:Universally unique identifier(eg. 123e4567-e89b-12d3-a456-426655440000)
#### 2.后端代碼實現(xiàn)
a.將生成圖像驗證碼的模塊文件夾(百度云盤有提供captcha文件夾)復制粘貼到項目根目錄utils文件夾下
b.由于驗證(圖片驗證冬竟、短信驗證)功能欧穴,以后有可能在其他應用或項目中重用,所以單獨創(chuàng)建一個應用來實現(xiàn)泵殴,所有驗證相關(guān)的業(yè)務邏輯接口涮帘。在apps目錄中創(chuàng)建一個verifications應用,并在settings.py文件中的INSTALLED_APPS列表中指定笑诅。
```python
# 在verifications/views.py文件中添加如下代碼:
import logging
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from django.http import HttpResponse
from utils.captcha.captcha import captcha
# 安裝圖片驗證碼所需要的 Pillow 模塊
# pip install Pillow
from . import constants
from users.models import Users
# 導入日志器
logger = logging.getLogger('django')
class ImageCode(View):
????"""
????define image verification view
????# /image_codes/<uuid:image_code_id>/
????"""
????def get(self, request, image_code_id):
????????text, image = captcha.generate_captcha()
????????# 確保settings.py文件中有配置redis CACHE
????????# Redis原生指令參考 http://redisdoc.com/index.html
????????# Redis python客戶端 方法參考 http://redis-py.readthedocs.io/en/latest/#indices-and-tables
????????con_redis = get_redis_connection(alias='verify_codes')
????????img_key = "img_{}".format(image_code_id).encode('utf-8')
????????# 將圖片驗證碼的key和驗證碼文本保存到redis中调缨,并設(shè)置過期時間
????????con_redis.setex(img_key, constants.IMAGE_CODE_REDIS_EXPIRES, text)
????????logger.info("Image code: {}".format(text))
????????return HttpResponse(content=image, content_type="images/jpg")
```
c.為了保存應用中用到的常量信息,需要在verifications應用下創(chuàng)建一個constants.py文件
```python
# 在verifications/constants.py文件中加入如下代碼:
# 圖片驗證碼redis有效期吆你,單位秒
IMAGE_CODE_REDIS_EXPIRES = 5 * 60
```
d.本項目需要將圖形驗證碼弦叶、短信驗證碼以及用戶的會話信息保存到redis服務器中,所以需要在settings.py文件中指定如下配置信息:
```python
# settings.py文件中加入如下內(nèi)容:
CACHES = {
????"default": {
????????"BACKEND": "django_redis.cache.RedisCache",??# 指定redis緩存后端
????????"LOCATION": "redis://127.0.0.1:6379/0",
????????"OPTIONS": {
????????????"CLIENT_CLASS": "django_redis.client.DefaultClient",
????????????# "PASSWORD": "mysecret"
????????}
????},
????# 同樣可以指定多個redis
????"session": {
????????"BACKEND": "django_redis.cache.RedisCache",
????????"LOCATION": "redis://127.0.0.1:6379/1",
????????"OPTIONS": {
????????????"CLIENT_CLASS": "django_redis.client.DefaultClient",
????????}
????},
????"verify_codes": {
????????"BACKEND": "django_redis.cache.RedisCache",
????????"LOCATION": "redis://127.0.0.1:6379/2",
????????"OPTIONS": {
????????????"CLIENT_CLASS": "django_redis.client.DefaultClient",
????????}
????},
????"sms_codes": {
????????"BACKEND": "django_redis.cache.RedisCache",
????????"LOCATION": "redis://127.0.0.1:6379/3",
????????"OPTIONS": {
????????????"CLIENT_CLASS": "django_redis.client.DefaultClient",
????????}
????},
}
# 將用戶的session保存到redis中
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# 指定緩存redis的別名
SESSION_CACHE_ALIAS = "session"
```
e.在verifications應用下創(chuàng)建一個urls.py文件并添加如下內(nèi)容:
```python
# verifications應用下創(chuàng)建一個urls.py
from django.urls import path, re_path
from . import views
app_name = "verifications"
urlpatterns = [
????# re_path(r'^image_codes/(?P<image_code_id>[\w-]+)/$', view=views.ImageCodeView.as_view(), name="image_code"),
????# image_code_id為uuid格式
????path('image_codes/<uuid:image_code_id>/', views.ImageCode.as_view(), name='image_code'),
]
```
#### 3.前端代碼實現(xiàn)
html代碼:
```jinja2
{# 繼承base基類模版 #}
{% extends 'base/base.html' %}
{% block link %}
{#??<link rel="stylesheet" href="../../static/css/users/auth.css">#}
??<link rel="stylesheet" href="{% static 'css/users/auth.css' %}">
{% endblock %}
{% block title %}
注冊
{% endblock %}
<!-- main-contain start??-->
{% block main_start %}
<main id="container">
??<div class="register-contain">
????<div class="top-contain">
??????<h4 class="please-register">請注冊</h4>
??????<a href="javascript:void(0);" class="login">立即登錄 ></a>
????</div>
????<form action="" method="post" class="form-contain">
??????<div class="form-item">
????????<input type="text" placeholder="請輸入用戶名" name="username" class="form-control"??autocomplete="off">
??????</div>
??????<div class="form-item">
????????<input type="password" placeholder="請輸入密碼" name="password" class="form-control">
??????</div>
??????<div class="form-item">
????????<input type="password" placeholder="請輸入確認密碼" name="password_repeat" class="form-control">
??????</div>
??????<div class="form-item">
????????<input type="tel" placeholder="請輸入手機號" name="telephone" class="form-control"??autocomplete="off" autofocus>
??????</div>
??????<div class="form-item">
????????<input type="text" placeholder="請輸入圖形驗證碼" name="captcha_graph" class="form-captcha">
????????<a href="javascript:void(0);" class="captcha-graph-img">
??????????<img src="" alt="驗證碼" title="點擊刷新" >
????????</a>
??????</div>
??????<div class="form-item">
????????<input type="text" placeholder="請輸入短信驗證碼" name="sms_captcha" class="form-captcha" autocomplete="off">
????????<a href="javascript:void(0);" class="sms-captcha" title="發(fā)送驗證碼">獲取短信驗證碼</a>
??????</div>
??????<div class="form-item">
????????<input type="submit" value="立即注冊" class="register-btn">
??????</div>
????</form>
??</div>
</main>
{% endblock %}
<!-- main-contain??end -->
{% block hot_recommend %}
{% endblock %}
{% block script %}
{#??<script src="/static/js/users/auth.js"></script>#}
??<script src="{% static 'js/users/auth.js' %}"></script>
{% endblock %}
```
在js文件夾下創(chuàng)建一個users文件夾用戶存放用戶模塊相關(guān)的js文件妇多,在users文件下創(chuàng)建auth.js文件湾蔓。
```javascript
$(function () {
??let $img = $(".form-item .captcha-graph-img img");??// 獲取圖像標簽
??let sImageCodeId = "";??// 定義圖像驗證碼ID值
??generateImageCode();??// 生成圖像驗證碼圖片
??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片
??// 生成一個圖片驗證碼的編號,并設(shè)置頁面中圖片驗證碼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;
??}
});
```
### 四咸包、判斷用戶名是否注冊功能實現(xiàn)
#### 1.分析
**請求方法**:**GET**
**url定義**:`/usernames/(?P<username>\w{5,20})/`
**請求參數(shù)**:url路徑參數(shù)
| 參數(shù)?????| 類型???| 前端是否必須傳 | 描述?????????????|
| -------- | ------ | -------------- | ---------------- |
| username | 字符串 | 是?????????????| 用戶輸入的用戶名 |
#### 2.后端代碼實現(xiàn)
```python
from utils.json_fun import to_json_data
from django.views import View
class CheckUsernameView(View):
????"""
????Check whether the user exists?
????GET usernames/(?P<username>\w{5,20})/
????"""
????def get(self, request, username):
????????# count = 1 if User.objects.get(username=username) else 0
????????data = {
????????????'username': username,
????????????'count': Users.objects.filter(username=username).count()
????????}
????????return to_json_data(data=data)
```
在項目根目錄中的utils目錄下創(chuàng)建json_fun.py文件桃序,用于處理json格式轉(zhuǎn)化功能。
```python
from django.http import JsonResponse
from .res_code import Code
def to_json_data(errno=Code.OK, errmsg='', data=None, kwargs=None):
????json_dict = {'errno': errno, 'errmsg': errmsg, 'data': data}
????if kwargs and isinstance(kwargs, dict) and kwargs.keys():
????????json_dict.update(kwargs)
????return JsonResponse(json_dict)
```
在項目根目錄中的utils目錄下創(chuàng)建res_code.py文件烂瘫,用于把后端執(zhí)行的情況返回給前端媒熊。
```python
class Code:
????OK??????????????????= "0"
????DBERR???????????????= "4001"
????NODATA??????????????= "4002"
????DATAEXIST???????????= "4003"
????DATAERR?????????????= "4004"
????METHERR?????????????= "4005"
????SMSERROR????????????= "4006"
????SMSFAIL?????????????= "4007"
????SESSIONERR??????????= "4101"
????LOGINERR????????????= "4102"
????PARAMERR????????????= "4103"
????USERERR?????????????= "4104"
????ROLEERR?????????????= "4105"
????PWDERR??????????????= "4106"
????SERVERERR???????????= "4500"
????UNKOWNERR???????????= "4501"
error_map = {
????Code.OK????????????????????: "成功",
????Code.DBERR?????????????????: "數(shù)據(jù)庫查詢錯誤",
????Code.NODATA????????????????: "無數(shù)據(jù)",
????Code.DATAEXIST?????????????: "數(shù)據(jù)已存在",
????Code.DATAERR???????????????: "數(shù)據(jù)錯誤",
????Code.METHERR???????????????: "方法錯誤",
????Code.SMSERROR??????????????: "發(fā)送短信驗證碼異常",
????Code.SMSFAIL???????????????: "發(fā)送短信驗證碼失敗",
????Code.SESSIONERR????????????: "用戶未登錄",
????Code.LOGINERR??????????????: "用戶登錄失敗",
????Code.PARAMERR??????????????: "參數(shù)錯誤",
????Code.USERERR???????????????: "用戶不存在或未激活",
????Code.ROLEERR???????????????: "用戶身份錯誤",
????Code.PWDERR????????????????: "密碼錯誤",
????Code.SERVERERR?????????????: "內(nèi)部錯誤",
????Code.UNKOWNERR?????????????: "未知錯誤",
}
```
```python
# url 定義
from django.urls import path, re_path
from . import views
app_name = "verifications"
urlpatterns = [
????# image_code_id為uuid格式
????path('image_codes/<uuid:image_code_id>/', views.ImageCode.as_view(), name='image_code'),
????re_path('usernames/(?P<username>\w{5,20})/', views.CheckUsernameView.as_view(), name='check_username'),
]
```
#### 3.前端代碼實現(xiàn)
```javascript
$(function () {
??let $username = $('#user_name');
??let $img = $(".form-item .captcha-graph-img img");
??let sImageCodeId = "";
??// 1、圖像驗證碼邏輯
??generateImageCode();??// 生成圖像驗證碼圖片
??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片
??// 2坟比、用戶名驗證邏輯
??$username.blur(function () {
????fn_check_usrname();
??});
??// 生成一個圖片驗證碼的編號芦鳍,并設(shè)置頁面中圖片驗證碼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;
??}
??// 判斷用戶名是否已經(jīng)注冊
??function fn_check_usrname() {
????let sUsername = $username.val();??// 獲取用戶名字符串
????if (sUsername === "") {
??????message.showError('用戶名不能為空!');
??????return
????}
????if (!(/^\w{5,20}$/).test(sUsername)) {
??????message.showError('請輸入5-20個字符的用戶名');
??????return
????}
????// 發(fā)送ajax請求籍琳,去后端查詢用戶名是否存在
????$.ajax({
??????url: '/usernames/' + sUsername + '/',
??????type: 'GET',
??????dataType: 'json',
????})
??????.done(function (res) {
????????if (res.data.count !== 0) {
??????????message.showError(res.data.username + '已注冊菲宴,請重新輸入贷祈!')
????????} else {
??????????message.showInfo(res.data.username + '能正常使用!')
????????}
??????})
??????.fail(function () {
????????message.showError('服務器超時喝峦,請重試势誊!');
??????});
??}
});
```
### 五、判斷手機號是否注冊功能實現(xiàn)
#### 1.分析
**請求方法**:**GET**
**url定義**:`/mobiles/(?P<mobile>1[3-9]\d{9})/`
**請求參數(shù)**:url路徑參數(shù)
| 參數(shù)???| 類型???| 前端是否必須傳 | 描述?????????????|
| ------ | ------ | -------------- | ---------------- |
| mobile | 字符串 | 是?????????????| 用戶輸入的手機號 |
#### 2.后端代碼實現(xiàn)
```python
# 在verifications目錄下的views.py文件中定義如下類視圖:
class CheckMobileView(View):
????"""
????Check whether the mobile exists
????GET mobiles/(?P<mobile>1[3-9]\d{9})/
????"""
????def get(self, request, mobile):
????????data = {
????????????'mobile': mobile,
????????????'count': Users.objects.filter(mobile=mobile).count()
????????}
????????return to_json_data(data=data)
```
```python
# 在verifications目錄下的urls.py文件中定義如下路由:
from django.urls import path, re_path
from . import views
app_name = "verifications"
urlpatterns = [
????re_path('mobiles/(?P<mobile>1[3-9]\d{9})/', views.CheckMobileView.as_view(), name='check_mobiles'),
]
```
#### 3.前端代碼實現(xiàn)
```python
$(function () {
??let $username = $('#user_name');??// 選擇id為user_name的網(wǎng)頁元素谣蠢,需要定義一個id為user_name
??let $img = $(".form-item .captcha-graph-img img");??// 獲取圖像標簽
??let sImageCodeId = "";??// 定義圖像驗證碼ID值
??let $mobile = $('#mobile');??// 選擇id為mobile的網(wǎng)頁元素粟耻,需要定義一個id為mobile
??// 1、圖像驗證碼邏輯
??generateImageCode();??// 生成圖像驗證碼圖片
??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片
??// 判斷用戶是否注冊
??// 2眉踱、用戶名驗證邏輯
??$username.blur(function () {
????fn_check_usrname();
??});
??// 3挤忙、手機號驗證邏輯
??// 判斷用戶手機號是否注冊
??$mobile.blur(function () {
????fn_check_mobile();
??});
??// 生成一個圖片驗證碼的編號,并設(shè)置頁面中圖片驗證碼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;
??}
??// 判斷用戶名是否已經(jīng)注冊
??function fn_check_usrname() {
????let sUsername = $username.val();??// 獲取用戶名字符串
????if (sUsername === "") {
??????message.showError('用戶名不能為空叁执!');
??????return
????}
????// test()方法 判斷字符串中是否匹配到正則表達式內(nèi)容,返回的是boolean值 ( true / false )
????if (!(/^\w{5,20}$/).test(sUsername)) {
??????message.showError('請輸入5-20個字符的用戶名');
??????return
????}
????// 發(fā)送ajax請求矮冬,去后端查詢用戶名是否存在
????$.ajax({
??????url: '/usernames/' + sUsername + '/',
??????type: 'GET',
??????dataType: 'json',
??????// data:{'code':300268}
????})
??????.done(function (res) {
????????if (res.data.count !== 0) {
??????????message.showError(res.data.username + '已注冊谈宛,請重新輸入!')
????????} else {
??????????message.showInfo(res.data.username + '能正常使用胎署!')
????????}
??????})
??????.fail(function () {
????????message.showError('服務器超時吆录,請重試!');
??????});
??}
??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????// 把async關(guān)掉
????})
??????.done(function (res) {
????????if (res.data.count !== 0) {
??????????message.showError(res.data.mobile + '已注冊巨坊,請重新輸入撬槽!')
??????????SreturnValue = ""
????????} else {
??????????SreturnValue = "success"
????????}
??????})
??????.fail(function () {
????????message.showError('服務器超時,請重試趾撵!');
????????SreturnValue = ""
??????});
????return SreturnValue
??}
});
```
### 六侄柔、發(fā)送手機短信驗證碼功能實現(xiàn)
#### 1.分析
業(yè)務處理流程:
-?判斷圖片驗證碼是否正確
-?判斷是否在60s內(nèi)有發(fā)送記錄
-?生成短信驗證碼
-?保存短信驗證碼與發(fā)送記錄
-?發(fā)送短信
**請求方法**:**POST**
**url定義**:`/sms_codes/`
**請求參數(shù)**:url路徑參數(shù)
| 參數(shù)??????????| 類型???| 前端是否必須傳 | 描述?????????????????????|
| ------------- | ------ | -------------- | ------------------------ |
| mobile????????| 字符串 | 是?????????????| 用戶輸入的手機號?????????|
| image_code_id | UUID???| 是?????????????| js生成的圖片uuid號???????|
| text??????????| 字符串 | 是?????????????| 用戶輸入的圖片驗證碼文本 |
注:由于是post請求,在向后端發(fā)起請求時占调,需要附帶csrf token
#### 2.后端代碼實現(xiàn)
```python
# 在verifications目錄下的views.py文件中定義如下類視圖:
import logging
import json
import random
import string
from django.views import View
from django_redis import get_redis_connection
from . import constants
from utils.json_fun import to_json_data
from utils.res_code import Code, error_map
from users.models import Users
from . import forms
from utils.yuntongxun.sms import CCP
# 導入日志器
logger = logging.getLogger('django')
class SmsCodesView(View):
????"""
????send mobile sms code
????POST /sms_codes/
????"""
????def post(self, request):
????????# 1暂题、
????????json_data = request.body
????????if not json_data:
????????????return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
????????# 將json轉(zhuǎn)化為dict
????????dict_data = json.loads(json_data.decode('utf8'))
????????# 2、
????????form = forms.CheckImgCodeForm(data=dict_data)
????????if form.is_valid():
????????????# 獲取手機號
????????????mobile = form.cleaned_data.get('mobile')
????????????# 3究珊、
????????????# 創(chuàng)建短信驗證碼內(nèi)容
????????????sms_num = ''.join([random.choice(string.digits) for _ in range(constants.SMS_CODE_NUMS)])
????????????# 將短信驗證碼保存到數(shù)據(jù)庫
????????????# 確保settings.py文件中有配置redis CACHE
????????????# Redis原生指令參考 http://redisdoc.com/index.html
????????????# Redis python客戶端 方法參考 http://redis-py.readthedocs.io/en/latest/#indices-and-tables
????????????# 4薪者、
????????????redis_conn = get_redis_connection(alias='verify_codes')
????????????pl = redis_conn.pipeline()
????????????# 創(chuàng)建一個在60s以內(nèi)是否有發(fā)送短信記錄的標記
????????????sms_flag_fmt = "sms_flag_{}".format(mobile)
????????????# 創(chuàng)建保存短信驗證碼的標記key
????????????sms_text_fmt = "sms_{}".format(mobile)
????????????# 此處設(shè)置為True會出現(xiàn)bug
????????????try:
????????????????pl.setex(sms_flag_fmt.encode('utf8'), constants.SEND_SMS_CODE_INTERVAL, 1)
????????????????pl.setex(sms_text_fmt.encode('utf8'), constants.SMS_CODE_REDIS_EXPIRES, sms_num)
????????????????# 讓管道通知redis執(zhí)行命令
????????????????pl.execute()
????????????except Exception as e:
????????????????logger.debug("redis 執(zhí)行出現(xiàn)異常:{}".format(e))
????????????????return to_json_data(errno=Code.UNKOWNERR, errmsg=error_map[Code.UNKOWNERR])
????????????logger.info("Sms code: {}".format(sms_num))
????????????# 發(fā)送短語驗證碼
????????????try:
????????????????result = CCP().send_template_sms(mobile,
?????????????????????????????????????????????????[sms_num, constants.SMS_CODE_YUNTX_EXPIRES],
?????????????????????????????????????????????????constants.SMS_CODE_TEMP_ID)
????????????except Exception as e:
????????????????logger.error("發(fā)送驗證碼短信[異常][ mobile: %s, message: %s ]" % (mobile, e))
????????????????return to_json_data(errno=Code.SMSERROR, errmsg=error_map[Code.SMSERROR])
????????????else:
????????????????if result == 0:
????????????????????logger.info("發(fā)送驗證碼短信[正常][ mobile: %s sms_code: %s]" % (mobile, sms_num))
????????????????????return to_json_data(errno=Code.OK, errmsg="短信驗證碼發(fā)送成功")
????????????????else:
????????????????????logger.warning("發(fā)送驗證碼短信[失敗][ mobile: %s ]" % mobile)
????????????????????return to_json_data(errno=Code.SMSFAIL, errmsg=error_map[Code.SMSFAIL])
????????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 to_json_data(errno=Code.PARAMERR, errmsg=err_msg_str)
```
```python
# 在verifications目錄下的forms.py文件中定義如下form表單:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
??@Time : 2018/12/3 4:18 PM?
??@Auth : Youkou?
??@Site : www.youkou.site
??@File : forms.py
??@IDE??: PyCharm
??@Edit : 2018/12/3
-------------------------------------------------
"""
from django import forms
from django.core.validators import RegexValidator
from django_redis import get_redis_connection
from users.models import Users
# 創(chuàng)建手機號的正則校驗器
mobile_validator = RegexValidator(r"^1[3-9]\d{9}$", "手機號碼格式不正確")
class CheckImgCodeForm(forms.Form):
????"""
????check image code
????"""
????mobile = forms.CharField(max_length=11, min_length=11, validators=[mobile_validator, ],
?????????????????????????????error_messages={"min_length": "手機號長度有誤", "max_length": "手機號長度有誤",
?????????????????????????????????????????????"required": "手機號不能為空"})
????image_code_id = forms.UUIDField(error_messages={"required": "圖片UUID不能為空"})
????text = forms.CharField(max_length=4, min_length=4,
???????????????????????????error_messages={"min_length": "圖片驗證碼長度有誤", "max_length": "圖片驗證碼長度有誤",
???????????????????????????????????????????"required": "圖片驗證碼不能為空"})
????# Cleaning and validating fields that depend on each other
????def clean(self):
????????cleaned_data = super().clean()
????????# 1、
????????image_uuid = cleaned_data.get("image_code_id")
????????image_text = cleaned_data.get("text")
????????mobile_num = cleaned_data.get("mobile")
????????# 2剿涮、
????????if Users.objects.filter(mobile=mobile_num).count():
????????????raise forms.ValidationError("手機號已注冊啸胧,請重新輸入")
????????# 確保settings.py文件中有配置redis CACHE
????????# Redis原生指令參考 http://redisdoc.com/index.html
????????# Redis python客戶端 方法參考 http://redis-py.readthedocs.io/en/latest/#indices-and-tables
????????# 2赶站、
????????con_redis = get_redis_connection(alias='verify_codes')
????????# 創(chuàng)建保存到redis中圖片驗證碼的key
????????img_key = "img_{}".format(image_uuid).encode('utf-8')
????????# 取出圖片驗證碼
????????real_image_code_origin = con_redis.get(img_key)
????????real_image_code = real_image_code_origin.decode('utf-8') if real_image_code_origin else None
????????con_redis.delete(img_key)
????????# 驗證手機號
????????if (not real_image_code) or (image_text != real_image_code):
????????????raise forms.ValidationError("圖片驗證失敗")
????????# 檢查是否在60s內(nèi)有發(fā)送記錄
????????sms_flag_fmt = "sms_flag_{}".format(mobile_num).encode('utf-8')
????????sms_flag = con_redis.get(sms_flag_fmt)
????????if sms_flag:
????????????raise forms.ValidationError("獲取手機短信驗證碼過于頻繁")
```
```python
# 在verifications目錄下的constants.py文件中定義如下常數(shù):
# 圖片驗證碼redis有效期,單位秒
IMAGE_CODE_REDIS_EXPIRES = 5 * 60
# 短信驗證碼有效期纺念,單位秒
SMS_CODE_REDIS_EXPIRES = 5 * 60
# 云通訊短信驗證碼過期時間(發(fā)送短信是顯示為5分鐘)
SMS_CODE_YUNTX_EXPIRES = 5
# 發(fā)送間隔
SEND_SMS_CODE_INTERVAL = 60
# 短信發(fā)送模板
SMS_CODE_TEMP_ID = 1
# 短信驗證碼位數(shù)
SMS_CODE_NUMS = 6
```
#### 3. 短信驗證碼平臺-云通訊
a.**本項目中使用的短信發(fā)送模塊為**[云通訊](<http://www.yuntongxun.com/>)平臺:
-?用戶發(fā)送短信(或語音)驗證碼的第三方平臺
-?[參考文檔地址](<http://www.yuntongxun.com/doc/ready/demo/1_4_1_2.html>)
b.**注冊登錄**
-?免費注冊登錄
-?贈送8元贝椿,用于測試
-?身份認證之后,才能正常使用
c.**獲取開發(fā)者相關(guān)參數(shù)**
![yuntongxun_1](../images/yuntongxun_1.jpg)
```python
_accountSid = '開發(fā)者主賬號中的ACCOUNT SID'
# 說明:主賬號Token陷谱,登陸云通訊網(wǎng)站后烙博,可在控制臺-應用中看到開發(fā)者主賬號AUTH TOKEN
_accountToken = '開發(fā)者主賬號中的AUTH TOKEN'
# 請使用管理控制臺首頁的APPID或自己創(chuàng)建應用的APPID
_appId = '開發(fā)者主賬號中的AppID(默認)'
# 說明:請求地址,生產(chǎn)環(huán)境配置成app.cloopen.com
_serverIP = 'sandboxapp.cloopen.com'
# 說明:請求端口 烟逊,生產(chǎn)環(huán)境為8883
_serverPort = "8883"
# 說明:REST API版本號保持不變
_softVersion = '2013-12-26'
```
![modify_parm](../images/modify_parms.jpg)
d.**設(shè)置測試賬號**
![edit_test_mobile](../images/yuntongxun_2.jpg)
#### 4.前端代碼實現(xiàn)
```javascript
$(function () {
??let $username = $('#user_name');??// 選擇id為user_name的網(wǎng)頁元素渣窜,需要定義一個id為user_name
??let $img = $(".form-item .captcha-graph-img img");??// 獲取圖像標簽
??let sImageCodeId = "";??// 定義圖像驗證碼ID值
??let $mobile = $('#mobile');??// 選擇id為mobile的網(wǎng)頁元素,需要定義一個id為mobile
??let $smsCodeBtn = $('.form-item .sms-captcha');??// 獲取短信驗證碼按鈕元素宪躯,需要定義一個id為input_smscode
??let $imgCodeText = $('#input_captcha');??// 獲取用戶輸入的圖片驗證碼元素乔宿,需要定義一個id為input_captcha
??// 1、圖像驗證碼邏輯
??generateImageCode();??// 生成圖像驗證碼圖片
??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片
??// 2访雪、判斷用戶名是否注冊
??$username.blur(function () {
????fn_check_usrname();
??});
??// 3详瑞、判斷用戶手機號是否注冊
??$mobile.blur(function () {
????fn_check_mobile();
??});
??// 4、發(fā)送短信邏輯
??$smsCodeBtn.click(function () {
????// 判斷手機號是否輸入
????if (fn_check_mobile() !== "success") {
??????return
????}
????// 判斷用戶是否輸入圖片驗證碼
????let text = $imgCodeText.val();??// 獲取用戶輸入的圖片驗證碼文本
????if (!text) {
????????message.showError('請?zhí)顚戲炞C碼臣缀!');
????????return
????}
????// 判斷是否生成的UUID
????if (!sImageCodeId) {
??????message.showError('圖片UUID為空');
??????return
????}
????// 正常獲取參數(shù)
????let SdataParams = {
??????"mobile": $mobile.val(),???// 獲取用戶輸入的手機號
??????"text": text,??// 獲取用戶輸入的圖片驗證碼文本
??????"image_code_id": sImageCodeId??// 獲取圖片UUID
????};
????// for test
????// let SdataParams = {
????//???"mobile": "1886608",???// 獲取用戶輸入的手機號
????//???"text": "ha3d",??// 獲取用戶輸入的圖片驗證碼文本
????//???"image_code_id": "680a5a66-d9e5-4c3c-b8ea"??// 獲取圖片UUID
????// };
????// 向后端發(fā)送請求
????$.ajax({
??????// 請求地址
??????url: "/sms_codes/",
??????// 請求方式
??????type: "POST",
??????data: JSON.stringify(SdataParams),
??????// 請求內(nèi)容的數(shù)據(jù)類型(前端發(fā)給后端的格式)
??????contentType: "application/json; charset=utf-8",
??????// 響應數(shù)據(jù)的格式(后端返回給前端的格式)
??????dataType: "json",
??????async: false????// 關(guān)掉異步功能
????})
??????.done(function (res) {
????????if (res.errno === "0") {
??????????// 倒計時60秒坝橡,60秒后允許用戶再次點擊發(fā)送短信驗證碼的按鈕
???????????message.showSuccess('短信驗證碼發(fā)送成功');
??????????let num = 60;
??????????// 設(shè)置一個計時器
??????????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('服務器超時,請重試精置!');
??????});
??});
??// 生成一個圖片驗證碼的編號计寇,并設(shè)置頁面中圖片驗證碼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;
??}
??// 判斷用戶名是否已經(jīng)注冊
??function fn_check_usrname() {
????let sUsername = $username.val();??// 獲取用戶名字符串
????let sReturnValue = "";
????if (sUsername === "") {
??????message.showError('用戶名不能為空!');
??????return
????}
????// test()方法 判斷字符串中是否匹配到正則表達式內(nèi)容赖阻,返回的是boolean值 ( true / false )
????if (!(/^\w{5,20}$/).test(sUsername)) {
??????message.showError('請輸入5-20個字符的用戶名');
??????return
????}
????// 發(fā)送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 = ""
????????}
??????})
??????.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 {
??????????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'));
??????}
????}
??});
});
```
### 七打却、用戶注冊功能實現(xiàn)
#### 1.分析
業(yè)務處理流程:
-?判斷用戶名是否為空,是否已注冊
-?判斷手機號是否為空谎倔,是否已注冊
-?判斷密碼是否為空柳击,格式是否正確
-?判斷確認密碼與密碼是否相同
-?判斷短信驗證碼是否為空,是否格式正確片习,是否與真實的短信驗證碼相同
**請求方法**:**POST**
**url定義**:`/users/register/`
**請求參數(shù)**:url路徑參數(shù)
| 參數(shù)????????????| 類型???| 前端是否必須傳 | 描述?????????????????|
| --------------- | ------ | -------------- | -------------------- |
| username????????| 字符串 | 是?????????????| 用戶輸入的用戶名?????|
| password????????| 字符串 | 是?????????????| 用戶輸入的密碼???????|
| password_repeat | 字符串 | 是?????????????| 用戶輸入的重復密碼???|
| mobile??????????| 字符串 | 是?????????????| 用戶輸入的手機號?????|
| sms_code????????| 字符串 | 是?????????????| 用戶輸入的短信驗證碼 |
注:由于是post請求捌肴,在向后端發(fā)起請求時,需要附帶csrf token
#### 2.后端代碼實現(xiàn)
```python
# 在users目錄下的views.py文件中定義如下類視圖:
import json
from django.shortcuts import render
from django.views import View
from .forms import RegisterForm
from .models import Users
from utils.json_fun import to_json_data
from utils.res_code import Code, error_map
class RegisterView(View):
????"""
????"""
????def get(self, request):
????????"""
????????"""
????????return render(request, 'users/register.html')
????def post(self, request):
????????"""
????????"""
????????# 1藕咏、
????????json_data = request.body
????????if not json_data:
????????????return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
????????# 將json轉(zhuǎn)化為dict
????????dict_data = json.loads(json_data.decode('utf8'))
????????form = RegisterForm(data=dict_data)
????????if form.is_valid():
????????????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)
????????????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)
```
```python
# 在users目錄下的forms.py文件中定義如下form表單:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
??@Time : 2018/12/8 2:22 PM?
??@Auth : Youkou?
??@Site : www.youkou.site
??@File : forms.py
??@IDE??: PyCharm
??@Edit : 2018/12/8
-------------------------------------------------
"""
import re
from django import forms
from django_redis import get_redis_connection
from verifications.constants import SMS_CODE_NUMS
from .models import Users
class RegisterForm(forms.Form):
????"""
????"""
????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):
????????"""
????????"""
????????tel = self.cleaned_data.get('mobile')
????????if not re.match(r"^1[3-9]\d{9}$", tel):
????????????raise forms.ValidationError("手機號碼格式不正確")
????????if Users.objects.filter(mobile=tel).exists():
????????????raise forms.ValidationError("手機號已注冊孽查,請重新輸入饥悴!")
????????return tel
????def clean(self):
????????"""
????????"""
????????#
????????cleaned_data = super().clean()
????????passwd = cleaned_data.get('password')
????????passwd_repeat = cleaned_data.get('password_repeat')
????????if passwd != passwd_repeat:
????????????#
????????????raise forms.ValidationError("兩次密碼不一致")
????????#
????????tel = cleaned_data.get('mobile')
????????sms_text = cleaned_data.get('sms_code')
????????# 建立redis連接
????????redis_conn = get_redis_connection(alias='verify_codes')
????????#
????????sms_fmt = "sms_{}".format(tel).encode('utf-8')
????????#
????????real_sms = redis_conn.get(sms_fmt)
????????#
????????if (not real_sms) or (sms_text != real_sms.decode('utf-8')):
????????????raise forms.ValidationError("短信驗證碼錯誤")
```
```python
# 在users目錄下的urls.py文件中添如下路由:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
??@Time : 2018/11/29 9:58 AM?
??@Auth : Youkou?
??@Site : www.youkou.site
??@File : urls.py
??@IDE??: PyCharm
??@Edit : 2018/11/29
-------------------------------------------------
"""
from django.urls import path
from . import views
app_name = 'users'
urlpatterns = [
????# path('register/', views.register, name='register'),
????path('register/', views.RegisterView.as_view(), name='register'),
]
```
#### 3.前端代碼實現(xiàn)
```javascript
$(function () {
??let $username = $('#user_name');??// 選擇id為user_name的網(wǎng)頁元素,需要定義一個id為user_name
??let $img = $(".form-item .captcha-graph-img img");??// 獲取圖像標簽
??let sImageCodeId = "";??// 定義圖像驗證碼ID值
??let $mobile = $('#mobile');??// 選擇id為mobile的網(wǎng)頁元素盲再,需要定義一個id為mobile
??let $smsCodeBtn = $('.form-item .sms-captcha');??// 獲取短信驗證碼按鈕元素西设,需要定義一個id為input_smscode
??let $imgCodeText = $('#input_captcha');??// 獲取用戶輸入的圖片驗證碼元素,需要定義一個id為input_captcha
??let $register = $('.form-contain');??// 獲取注冊表單元素
??// 1洲胖、圖片驗證碼邏輯
??// 通過uuid生成驗證碼編號
??// 拼接驗證碼地址
??// 設(shè)置驗證碼圖片標簽的src
??generateImageCode();??// 生成圖像驗證碼圖片
??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片
??// 2济榨、用戶名驗證邏輯
??$username.blur(function () {
????fn_check_usrname();
??});
??// 3坯沪、手機號驗證邏輯
??// 判斷用戶手機號是否注冊
??$mobile.blur(function () {
????fn_check_mobile();
??});
??// 4绿映、發(fā)送短信驗證碼邏輯
??$smsCodeBtn.click(function () {
????// 判斷手機號是否輸入
????if (fn_check_mobile() !== "success") {
??????return
????}
????// 判斷用戶是否輸入圖片驗證碼
????let text = $imgCodeText.val();??// 獲取用戶輸入的圖片驗證碼文本
????if (!text) {
????????message.showError('請?zhí)顚戲炞C碼!');
????????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
????// };
????// 向后端發(fā)送請求
????$.ajax({
??????// 請求地址
??????url: "/sms_codes/",
??????// 請求方式
??????type: "POST",
??????// 向后端發(fā)送csrf token
??????// headers: {
??????//???????????// 根據(jù)后端開啟的CSRFProtect保護腐晾,cookie字段名固定為X-CSRFToken
??????//???????????"X-CSRFToken": getCookie("csrf_token")
??????// },
??????// data: JSON.stringify(SdataParams),
??????data: JSON.stringify(SdataParams),
??????// 請求內(nèi)容的數(shù)據(jù)類型(前端發(fā)給后端的格式)
??????contentType: "application/json; charset=utf-8",
??????// 響應數(shù)據(jù)的格式(后端返回給前端的格式)
??????dataType: "json",
??????async: false
????})
??????.done(function (res) {
????????if (res.errno === "0") {
??????????// 倒計時60秒叉弦,60秒后允許用戶再次點擊發(fā)送短信驗證碼的按鈕
???????????message.showSuccess('短信驗證碼發(fā)送成功');
??????????let num = 60;
??????????// 設(shè)置一個計時器
??????????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();
????// 獲取用戶輸入的內(nèi)容
????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位以內(nèi)');
??????return
????}
????// 判斷用戶輸入的密碼和確認密碼是否一致
????if (sPassword !== sPasswordRepeat) {
??????message.showError('密碼和確認密碼不一致');
??????return
????}
????// 判斷用戶輸入的短信驗證碼是否為6位數(shù)字
????if (!(/^\d{6}$/).test(sSmsCode)) {
??????message.showError('短信驗證碼格式不正確巨柒,必須為6位數(shù)字樱拴!');
??????return
????}
????// 發(fā)起注冊請求
????// 1、創(chuàng)建請求參數(shù)
????let SdataParams = {
??????"username": sUsername,
??????"password": sPassword,
??????"password_repeat": sPasswordRepeat,
??????"mobile": sMobile,
??????"sms_code": sSmsCode
????};
????// 2洋满、創(chuàng)建ajax請求
????$.ajax({
??????// 請求地址
??????url: "/users/register/",??// url尾部需要添加/
??????// 請求方式
??????type: "POST",
??????data: JSON.stringify(SdataParams),
??????// 請求內(nèi)容的數(shù)據(jù)類型(前端發(fā)給后端的格式)
??????contentType: "application/json; charset=utf-8",
??????// 響應數(shù)據(jù)的格式(后端返回給前端的格式)
??????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('服務器超時正罢,請重試!');
??????});
??});
??// 生成一個圖片驗證碼的編號驻民,并設(shè)置頁面中圖片驗證碼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;
??}
??// 判斷用戶名是否已經(jīng)注冊
??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
????}
????// 發(fā)送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'));
??????}
????}
??});
});
```