1. 程式人生 > >Django商城項目筆記No.5用戶部分-註冊接口-短信驗證碼

Django商城項目筆記No.5用戶部分-註冊接口-短信驗證碼

change 路由 筆記 window 管道 || The app url

Django商城項目筆記No.4用戶部分-註冊接口-短信驗證碼

技術分享圖片

技術分享圖片

技術分享圖片

短信驗證碼也保存在redis裏(sms_code_15101234567)

在views中新增SMSCodeView類視圖,並且寫出步驟如下:

技術分享圖片

寫邏輯

技術分享圖片

因為這裏要用到序列化器,所以就繼承了GenericAPIView,這樣的話,可以指定serializer_class,然後通過get_serializer就可以實例化序列化器了。

註意 既然繼承了GenericAPIView,那為啥不設置queryset屬性,只設置了serializer_class屬性了呢?

這裏用到誰,就設置誰。GenericAPIView沒有要求必須都設置的。

發送短信驗證碼序列化器實現

讓序列化器對於參數做校驗,只校驗image_code_id和text即可,手機號是在路由中,手機號不對的話也進不來這個視圖函數

技術分享圖片

最後一個邏輯,判斷是否在60s內,是如何獲取mobile的,分析如下:

首先先來看下序列化器的context屬性:

技術分享圖片

在自己創建序列化器的時候,可以指定context,來傳遞數據。

那麽GenericAPIView提供的get_serializer方法在創建序列化器的時候,也指定了context,並且給context添加了三個數據:

技術分享圖片

所以如下

技術分享圖片

發送短信驗證碼視圖實現

技術分享圖片

發送間隔常量如下:

技術分享圖片

send_flag標記問題

控制前端請求過於頻繁

技術分享圖片

設置了send_flag標記之後,就算用戶繞過前端,使用postman之類的工具,也無法頻繁請求發送短信

問題:那能不能不要這個標記,只存儲sms_xxx行不行,利用他的有效期來處理這個邏輯行不?

不行的,因為短信驗證碼的有效期是:5分鐘。

為什麽短信驗證碼有效期是5分鐘,自己思考

技術分享圖片

使用雲通訊發送短信驗證碼

接下來發送短信驗證碼,但是需要用到雲通訊的ccp,所以需要引入雲通訊工具:

將資料中的yuntongxun文件夾,直接copyutils

技術分享圖片

然後修改一下配置信息:

技術分享圖片

代碼實現

loger實現

import logging
logger = logging.getLogger(
django)

技術分享圖片

短信模板id

技術分享圖片

url配置

技術分享圖片

redis管道

保存短信驗證碼和發送記錄的時候,做了兩次redis的操作

技術分享圖片

改為管道

技術分享圖片

補充刪除圖片驗證碼的邏輯

避免用戶拿著同一個圖片驗證碼,獲取多次短信驗證碼。

如何避免呢?在獲取到圖片驗證碼之後,將redis中的圖片驗證碼刪除。

技術分享圖片

發送短信前端代碼說明與測試跨域請求

發送短信驗證碼前端代碼如下:

技術分享圖片
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>美多商城-註冊</title>
    <link rel="stylesheet" type="text/css" href="css/reset.css">
    <link rel="stylesheet" type="text/css" href="css/main.css">
    <script type="text/javascript" src="js/host.js"></script>
    <script type="text/javascript" src="js/vue-2.5.16.js"></script>
    <script type="text/javascript" src="js/axios-0.18.0.min.js"></script>
</head>
<body>
    <div class="register_con">
        <div class="l_con fl">
            <a class="reg_logo"><img src="images/logo.png"></a>
            <div class="reg_slogan">商品美 · 種類多 · 歡迎光臨</div>
            <div class="reg_banner"></div>
        </div>

        <div class="r_con fr">
            <div class="reg_title clearfix">
                <h1>用戶註冊</h1>
                <a href="/login.html">登錄</a>
            </div>
            <div class="reg_form clearfix" id="app" v-cloak>
                <form id="reg_form" @submit.prevent="on_submit">
                <ul>
                    <li>
                        <label>用戶名:</label>
                        <input type="text" v-model="username" @blur="check_username" name="user_name" id="user_name">
                        <span v-show="error_name" class="error_tip">{{ error_name_message }}</span>
                    </li>                    
                    <li>
                        <label>密碼:</label>
                        <input type="password" v-model="password" @blur="check_pwd" name="pwd" id="pwd">
                        <span v-show="error_password" class="error_tip">密碼最少8位,最長20位</span>
                    </li>
                    <li>
                        <label>確認密碼:</label>
                        <input type="password" v-model="password2" @blur="check_cpwd" name="cpwd" id="cpwd">
                        <span v-show="error_check_password" class="error_tip">兩次輸入的密碼不一致</span>
                    </li>
                    <li>
                        <label>手機號:</label>
                        <input type="text" v-model="mobile" @blur="check_phone" name="phone" id="phone">
                        <span v-show="error_phone" class="error_tip">{{ error_phone_message }}</span>
                    </li>
                    <li>
                        <label>圖形驗證碼:</label>
                        <input type="text" v-model="image_code" @blur="check_image_code" name="pic_code" id="pic_code" class="msg_input">
                        <img :src="image_code_url" @click="generate_image_code" alt="圖形驗證碼" class="pic_code">
                        <span v-show="error_image_code" class="error_tip">{{ error_image_code_message }}</span>
                    </li>
                    <li>
                        <label>短信驗證碼:</label>
                        <input type="text" v-model="sms_code" @blur="check_sms_code" name="msg_code" id="msg_code" class="msg_input">
                        <a @click="send_sms_code" class="get_msg_code">{{ sms_code_tip }}</a>
                        <span v-show="error_sms_code" class="error_tip">{{ error_sms_code_message }}</span>
                    </li>
                    <li class="agreement">
                        <input type="checkbox" v-model="allow" @change="check_allow" name="allow" id="allow">
                        <label>同意”美多商城用戶使用協議“</label>
                        <span v-show="error_allow" class="error_tip2">請勾選同意</span>
                    </li>
                    <li class="reg_sub">
                        <input type="submit" value="註 冊" name="">
                    </li>
                </ul>                
                </form>
            </div>

        </div>

    </div>

    <div class="footer no-mp">
        <div class="foot_link">
            <a href="#">關於我們</a>
            <span>|</span>
            <a href="#">聯系我們</a>
            <span>|</span>
            <a href="#">招聘人才</a>
            <span>|</span>
            <a href="#">友情鏈接</a>        
        </div>
        <p>CopyRight ? 2016 北京美多商業股份有限公司 All Rights Reserved</p>
        <p>電話:010-****888    京ICP備*******8號</p>
    </div>
    <script type="text/javascript" src="js/register.js"></script>
</body>
</html>
View Code

register.js代碼

技術分享圖片
var vm = new Vue({
    el: ‘#app‘,
    data: {
        host: host,

        error_name: false,
        error_password: false,
        error_check_password: false,
        error_phone: false,
        error_allow: false,
        error_image_code: false,
        error_sms_code: false,
        error_name_message: ‘‘,
        error_image_code_message: ‘‘,
        error_phone_message: ‘‘,
        error_sms_code_message: ‘‘,

        image_code_id: ‘‘, // 圖片驗證碼id
        image_code_url: ‘‘,

        sms_code_tip: ‘獲取短信驗證碼‘,
        sending_flag: false, // 正在發送短信標誌

        username: ‘‘,
        password: ‘‘,
        password2: ‘‘,
        mobile: ‘‘, 
        image_code: ‘‘,
        sms_code: ‘‘,
        allow: false
    },
    mounted: function(){
        this.generate_image_code();
    },
    methods: {
        // 生成uuid
        generate_uuid: function(){
            var d = new Date().getTime();
            if(window.performance && typeof window.performance.now === "function"){
                d += performance.now(); //use high-precision timer if available
            }
            var uuid = ‘xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx‘.replace(/[xy]/g, function(c) {
                var r = (d + Math.random()*16)%16 | 0;
                d = Math.floor(d/16);
                return (c ==‘x‘ ? r : (r&0x3|0x8)).toString(16);
            });
            return uuid;
        },
        // 生成一個圖片驗證碼的編號,並設置頁面中圖片驗證碼img標簽的src屬性
        generate_image_code: function(){
            // 生成一個編號
            // 嚴格一點的使用uuid保證編號唯一, 不是很嚴謹的情況下,也可以使用時間戳
            this.image_code_id = this.generate_uuid();

            // 設置頁面中圖片驗證碼img標簽的src屬性
            this.image_code_url = this.host + "/image_codes/" + this.image_code_id + "/";
        },
        check_username: function (){
            var len = this.username.length;
            if(len<5||len>20) {
                this.error_name_message = ‘請輸入5-20個字符的用戶名‘;
                this.error_name = true;
            } else {
                this.error_name = false;
            }

        },
        check_pwd: function (){
            var len = this.password.length;
            if(len<8||len>20){
                this.error_password = true;
            } else {
                this.error_password = false;
            }        
        },
        check_cpwd: function (){
            if(this.password!=this.password2) {
                this.error_check_password = true;
            } else {
                this.error_check_password = false;
            }        
        },
        check_phone: function (){
            var re = /^1[345789]\d{9}$/;
            if(re.test(this.mobile)) {
                this.error_phone = false;
            } else {
                this.error_phone_message = ‘您輸入的手機號格式不正確‘;
                this.error_phone = true;
            }

        },
        check_image_code: function (){
            if(!this.image_code) {
                this.error_image_code_message = ‘請填寫圖片驗證碼‘;
                this.error_image_code = true;
            } else {
                this.error_image_code = false;
            }    
        },
        check_sms_code: function(){
            if(!this.sms_code){
                this.error_sms_code_message = ‘請填寫短信驗證碼‘;
                this.error_sms_code = true;
            } else {
                this.error_sms_code = false;
            }
        },
        check_allow: function(){
            if(!this.allow) {
                this.error_allow = true;
            } else {
                this.error_allow = false;
            }
        },
        // 發送手機短信驗證碼
        send_sms_code: function(){
            if (this.sending_flag == true) {
                return;
            } 
            this.sending_flag = true;

            // 校驗參數,保證輸入框有數據填寫
            this.check_phone();
            this.check_image_code();

            if (this.error_phone == true || this.error_image_code == true) {
                this.sending_flag = false;
                return;
            }

            // 向後端接口發送請求,讓後端發送短信驗證碼
            axios.get(this.host + ‘/sms_codes/‘ + this.mobile + ‘/?text=‘ + this.image_code+‘&image_code_id=‘+ this.image_code_id, {
                    responseType: ‘json‘
                })
                .then(response => {
                    // 表示後端發送短信成功
                    // 倒計時60秒,60秒後允許用戶再次點擊發送短信驗證碼的按鈕
                    var num = 60;
                    // 設置一個計時器
                    var t = setInterval(() => {
                        if (num == 1) {
                            // 如果計時器到最後, 清除計時器對象
                            clearInterval(t);
                            // 將點擊獲取驗證碼的按鈕展示的文本回復成原始文本
                            this.sms_code_tip = ‘獲取短信驗證碼‘;
                            // 將點擊按鈕的onclick事件函數恢復回去
                            this.sending_flag = false;
                        } else {
                            num -= 1;
                            // 展示倒計時信息
                            this.sms_code_tip = num + ‘秒‘;
                        }
                    }, 1000, 60)
                })
                .catch(error => {
                    if (error.response.status == 400) {
                        this.error_image_code_message = ‘圖片驗證碼有誤‘;
                        this.error_image_code = true;
                        this.generate_image_code();
                    } else {
                        console.log(error.response.data);
                    }
                    this.sending_flag = false;
                })
        },
        // 註冊
        on_submit: function(){
            this.check_username();
            this.check_pwd();
            this.check_cpwd();
            this.check_phone();
            this.check_sms_code();
            this.check_allow();


        }
    }
});
View Code

訪問前端register.html,輸入手機號和驗證碼,點擊發送短信驗證碼,發現報錯:

技術分享圖片

缺少axios,那就copy一個過來:

技術分享圖片

然後重新測試,報錯信息如下:

技術分享圖片

跨域請求未解決。

Django商城項目筆記No.5用戶部分-註冊接口-短信驗證碼