路飛專案六
阿新 • • 發佈:2020-07-27
1 簡訊介面頻率限制
# throttlings.py from rest_framework.throttling import SimpleRateThrottle class SMSThrotting(SimpleRateThrottle): scope = 'sms' def get_cache_key(self, request, view): telephone = request.query_params.get('telephone') #'throttle_%(scope)s_%(ident)s'%{} return self.cache_format%{'scope':self.scope,'ident':telephone} # 配置在檢視類上 from .throttlings import SMSThrotting class SendSmSView(ViewSet): throttle_classes = [SMSThrotting,] # 早setting中配置 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES':{ 'sms':'1/m' # key要跟類中的scop對應 } }
2 驗證碼登陸介面
# views.py @action(methods=['POST'],detail=False) def code_login(self,request,*args,**kwargs): ser = serializer.CodeUserSerilaizer(data=request.data) if ser.is_valid(): token = ser.context['token'] username = ser.context['user'].username return APIResponse(token=token, username=username) else: return APIResponse(code=0,msg=ser.errors) # serializer class CodeUserSerilaizer(serializers.ModelSerializer): code=serializers.CharField() class Meta: model = models.User fields = ['telephone', 'code'] def validate(self, attrs): user=self._get_user(attrs) # 使用者存在,簽發token token = self._get_token(user) self.context['token'] = token self.context['user'] = user return attrs def _get_user(self, attrs): from django.core.cache import cache from django.conf import settings import re telephone = attrs.get('telephone') code = attrs.get('code') # 取出原來的code cache_code=cache.get(settings.PHONE_CACHE_KEY%telephone) if code ==cache_code: # 驗證碼通過 if re.match('^1[3-9][0-9]{9}$', telephone): user = models.User.objects.filter(telephone=telephone).first() if user: # 把使用過的驗證碼刪除 cache.set(settings.PHONE_CACHE_KEY % telephone,'') return user else: raise ValidationError('使用者不存在') else: raise ValidationError('手機號不合法') else: raise ValidationError('驗證碼錯誤') def _get_token(self, user): from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler payload = jwt_payload_handler(user) # 通過user物件獲得payload token = jwt_encode_handler(payload) # 通過payload獲得token return token
3 前臺傳送驗證碼
<template> <div class="login"> <div class="box"> <i class="el-icon-close" @click="close_login"></i> <div class="content"> <div class="nav"> <span :class="{active: login_method === 'is_pwd'}" @click="change_login_method('is_pwd')">密碼登入</span> <span :class="{active: login_method === 'is_sms'}" @click="change_login_method('is_sms')">簡訊登入</span> </div> <el-form v-if="login_method === 'is_pwd'"> <el-input placeholder="使用者名稱/手機號/郵箱" prefix-icon="el-icon-user" v-model="username" clearable> </el-input> <el-input placeholder="密碼" prefix-icon="el-icon-key" v-model="password" clearable show-password> </el-input> <el-button type="primary" @click="login_password">登入</el-button> </el-form> <el-form v-if="login_method === 'is_sms'"> <el-input placeholder="手機號" prefix-icon="el-icon-phone-outline" v-model="mobile" clearable @blur="check_mobile"> </el-input> <el-input placeholder="驗證碼" prefix-icon="el-icon-chat-line-round" v-model="sms" clearable> <template slot="append"> <span class="sms" @click="send_sms">{{ sms_interval }}</span> </template> </el-input> <el-button type="primary" @click="code_login">登入</el-button> </el-form> <div class="foot"> <span @click="go_register">立即註冊</span> </div> </div> </div> </div> </template> <script> export default { name: "Login", data() { return { username: '', password: '', mobile: '', sms: '', login_method: 'is_pwd', sms_interval: '獲取驗證碼', is_send: false, } }, methods: { close_login() { this.$emit('close') }, go_register() { this.$emit('go') }, change_login_method(method) { this.login_method = method; }, check_mobile() { if (!this.mobile) return; //字串.match(/正則表示式/) if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) { this.$message({ message: '手機號有誤', type: 'warning', duration: 1000, onClose: () => { this.mobile = ''; } }); return false; } //傳送axios請求 // this.$axios.get(this.$settings.base_url+'/user/check_telephone/telephone='+this.mobile}) this.$axios.get(this.$settings.base_url + '/user/check_telephone/', {params: {telephone: this.mobile}}).then(response => { if (response.data.code) { //手機號存在,允許傳送驗證碼 this.is_send = true; } else { this.$message({ message: '手機號不存在', type: 'warning', duration: 1000, onClose: () => { this.mobile = ''; } }); } }).catch(error => { console.log(error) }) }, send_sms() { if (!this.is_send) return; this.is_send = false; let sms_interval_time = 60; this.sms_interval = "傳送中..."; this.$axios.get(this.$settings.base_url + '/user/send/', {params: {'telephone': this.mobile}}) .then(response => { if (response.data.code) { this.$message({ message: '傳送驗證碼成功', type: 'success', duration: 1000, }); } }) // setInterval(()=>{},100) //定時器:每隔一秒種,把數字減一,當減到小於1,按鈕又能點了,顯示獲取驗證碼 let timer = setInterval(() => { if (sms_interval_time <= 1) { clearInterval(timer); //如果小於等於1,把定時器清除 this.sms_interval = "獲取驗證碼"; this.is_send = true; // 重新回覆點擊發送功能的條件 } else { sms_interval_time -= 1; this.sms_interval = `${sms_interval_time}秒後再發`; } }, 1000); }, login_password() { if (this.username && this.password) { //傳送請求 this.$axios.post(this.$settings.base_url + '/user/login/', { username: this.username, password: this.password }).then(response => { console.log(response.data) //把使用者資訊儲存到cookie中 // this.$cookies.set('key','value','過期時間,按s計') this.$cookies.set('token', response.data.token, '7d') this.$cookies.set('username', response.data.username, '7d') //關閉登入視窗(子傳父) this.$emit('close') //給父元件,Head傳遞一個事件,讓它從cookie中取出token和username this.$emit('loginsuccess') }).catch(errors => { }) } else { this.$message({ message: '使用者名稱或密碼必須填哦', type: 'warning', }); } }, code_login() { if (this.mobile && this.sms) { //傳送請求 this.$axios.post(this.$settings.base_url + '/user/code_login/', { telephone: this.mobile, code: this.sms }).then(response => { console.log(response.data) //把使用者資訊儲存到cookie中 // this.$cookies.set('key','value','過期時間,按s計') this.$cookies.set('token', response.data.token, '7d') this.$cookies.set('username', response.data.username, '7d') //關閉登入視窗(子傳父) this.$emit('close') //給父元件,Head傳遞一個事件,讓它從cookie中取出token和username this.$emit('loginsuccess') }).catch(errors => { }) } else { this.$message({ message: '手機號或驗證碼必填', type: 'warning', }); } }, } } </script> <style scoped> .login { width: 100vw; height: 100vh; position: fixed; top: 0; left: 0; z-index: 10; background-color: rgba(0, 0, 0, 0.3); } .box { width: 400px; height: 420px; background-color: white; border-radius: 10px; position: relative; top: calc(50vh - 210px); left: calc(50vw - 200px); } .el-icon-close { position: absolute; font-weight: bold; font-size: 20px; top: 10px; right: 10px; cursor: pointer; } .el-icon-close:hover { color: darkred; } .content { position: absolute; top: 40px; width: 280px; left: 60px; } .nav { font-size: 20px; height: 38px; border-bottom: 2px solid darkgrey; } .nav > span { margin: 0 20px 0 35px; color: darkgrey; user-select: none; cursor: pointer; padding-bottom: 10px; border-bottom: 2px solid darkgrey; } .nav > span.active { color: black; border-bottom: 3px solid black; padding-bottom: 9px; } .el-input, .el-button { margin-top: 40px; } .el-button { width: 100%; font-size: 18px; } .foot > span { float: right; margin-top: 20px; color: orange; cursor: pointer; } .sms { color: orange; cursor: pointer; display: inline-block; width: 70px; text-align: center; user-select: none; } </style>
4 前臺驗證碼登陸
#詳細程式碼看第三條,前臺傳送驗證碼
5 後臺註冊介面
# urls.py
router.register('register', views.RegisterView, 'register') # /user/register post請求就是新增
# views.py
class RegisterView(GenericViewSet,CreateModelMixin):
queryset = models.User.objects.all()
serializer_class = serializer.UserRegisterSerilaizer
def create(self, request, *args, **kwargs):
response=super().create(request, *args, **kwargs)
username=response.data.get('username')
return APIResponse(code=1,msg='註冊成功',username=username)
# serializer.py
class UserRegisterSerilaizer(serializers.ModelSerializer):
code=serializers.CharField(max_length=4,min_length=4,write_only=True)
class Meta:
model = models.User
fields = ['telephone', 'code','password','username']
extra_kwargs = {
'password': {'max_length': 18,'min_length':8},
'username': {'read_only':True}
}
def validate(self, attrs):
telephone = attrs.get('telephone')
code = attrs.get('code')
# 取出原來的code
cache_code = cache.get(settings.PHONE_CACHE_KEY % telephone)
if code == cache_code:
# 驗證碼通過
if re.match('^1[3-9][0-9]{9}$', telephone):
attrs['username']=telephone # 把使用者的名字設成手機號
attrs.pop('code')
return attrs
else:
raise ValidationError('手機號不合法')
else:
raise ValidationError('驗證碼錯誤')
# 重寫create方法
def create(self, validated_data):
user=models.User.objects.create_user(**validated_data)
return user
6 前臺註冊功能
# Register.vue
<template>
<div class="register">
<div class="box">
<i class="el-icon-close" @click="close_register"></i>
<div class="content">
<div class="nav">
<span class="active">新使用者註冊</span>
</div>
<el-form>
<el-input
placeholder="手機號"
prefix-icon="el-icon-phone-outline"
v-model="mobile"
clearable
@blur="check_mobile">
</el-input>
<el-input
placeholder="密碼"
prefix-icon="el-icon-key"
v-model="password"
clearable
show-password>
</el-input>
<el-input
placeholder="驗證碼"
prefix-icon="el-icon-chat-line-round"
v-model="sms"
clearable>
<template slot="append">
<span class="sms" @click="send_sms">{{ sms_interval }}</span>
</template>
</el-input>
<el-button type="primary" @click="register">註冊</el-button>
</el-form>
<div class="foot">
<span @click="go_login">立即登入</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Register",
data() {
return {
mobile: '',
password: '',
sms: '',
sms_interval: '獲取驗證碼',
is_send: false,
}
},
methods: {
close_register() {
this.$emit('close', false)
},
go_login() {
this.$emit('go')
},
check_mobile() {
if (!this.mobile) return;
//字串.match(/正則表示式/)
if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
this.$message({
message: '手機號有誤',
type: 'warning',
duration: 1000,
onClose: () => {
this.mobile = '';
}
});
return false;
}
this.$axios.get(this.$settings.base_url + '/user/check_telephone/', {params: {telephone: this.mobile}}).then(response => {
if (response.data.code) {
this.$message({
message: '您已經註冊過了,快去登入把',
type: 'warning',
duration: 1000,
onClose: () => {
this.go_login()
}
});
} else {
this.is_send = true;
this.$message({
message: '該使用者沒有註冊過,歡迎註冊我們的平臺',
type: 'success',
duration: 1000,
});
}
}).catch(error => {
console.log(error)
})
},
send_sms() {
if (!this.is_send) return;
this.is_send = false;
let sms_interval_time = 60;
this.sms_interval = "傳送中...";
this.$axios.get(this.$settings.base_url + '/user/send/', {params: {'telephone': this.mobile}})
.then(response => {
if (response.data.code) {
this.$message({
message: '傳送驗證碼成功',
type: 'success',
duration: 1000,
});
}
})
// setInterval(()=>{},100)
//定時器:每隔一秒種,把數字減一,當減到小於1,按鈕又能點了,顯示獲取驗證碼
let timer = setInterval(() => {
if (sms_interval_time <= 1) {
clearInterval(timer); //如果小於等於1,把定時器清除
this.sms_interval = "獲取驗證碼";
this.is_send = true; // 重新回覆點擊發送功能的條件
} else {
sms_interval_time -= 1;
this.sms_interval = `${sms_interval_time}秒後再發`;
}
}, 1000);
},
register() {
if (this.mobile && this.sms && this.password) {
this.$axios.post(this.$settings.base_url + '/user/register/', {
telephone: this.mobile,
code: this.sms,
password: this.password
}).then(response => {
if (response.data.code) {
//註冊成功,來個提示,跳轉到登入
this.$message({
message: '註冊成功',
type: 'success',
duration: 1000,
onClose: () => {
this.go_login()
}
});
} else {
this.$message({
message: '未知錯誤',
type: 'error',
duration: 1000,
onClose: () => {
this.mobile = ''
this.sms = ''
this.password = ''
}
});
}
})
} else {
this.$message({
message: '你有沒填的資訊',
type: 'error',
duration: 1000,
});
}
},
}
}
</script>
<style scoped>
.register {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 10;
background-color: rgba(0, 0, 0, 0.3);
}
.box {
width: 400px;
height: 480px;
background-color: white;
border-radius: 10px;
position: relative;
top: calc(50vh - 240px);
left: calc(50vw - 200px);
}
.el-icon-close {
position: absolute;
font-weight: bold;
font-size: 20px;
top: 10px;
right: 10px;
cursor: pointer;
}
.el-icon-close:hover {
color: darkred;
}
.content {
position: absolute;
top: 40px;
width: 280px;
left: 60px;
}
.nav {
font-size: 20px;
height: 38px;
border-bottom: 2px solid darkgrey;
}
.nav > span {
margin-left: 90px;
color: darkgrey;
user-select: none;
cursor: pointer;
padding-bottom: 10px;
border-bottom: 2px solid darkgrey;
}
.nav > span.active {
color: black;
border-bottom: 3px solid black;
padding-bottom: 9px;
}
.el-input, .el-button {
margin-top: 40px;
}
.el-button {
width: 100%;
font-size: 18px;
}
.foot > span {
float: right;
margin-top: 20px;
color: orange;
cursor: pointer;
}
.sms {
color: orange;
cursor: pointer;
display: inline-block;
width: 70px;
text-align: center;
user-select: none;
}
</style>
7 redis
#1 key-value的儲存方式,value有很多資料型別:5大:string(字串)、list(連結串列)、set(集合)、zset(sorted set --有序集合)和hash(雜湊型別
#2 記憶體資料庫
#3 與Memcached比較:
-1 Memcached只支援一種資料型別字串
-2 Memcached不支援持久化(不支援存到硬碟上,只要一斷電,資料就沒了)
# 4 使用Redis有哪些好處?
(1) 速度快,因為資料存在記憶體中,類似於HashMap,HashMap的優勢就是查詢和操作的時間複雜度都是O(1)
(2) 支援豐富資料型別,支援string,list,set,sorted set,hash
(3) 支援事務,操作都是原子性,所謂的原子性就是對資料的更改要麼全部執行,要麼全部不執行
(4) 豐富的特性:可用於快取,訊息,按key設定過期時間,過期後將會自動刪除
# 5 單執行緒,單程序,不存在併發訪問的問題(新版本已經不是了)
-單執行緒為什麼這麼快
-資料在記憶體(最重要的)
-io多路複用技術
-因為沒有程序,執行緒間的切換
# 6 redis適合的場景
1 排行榜
2 網站訪問量,文章訪問量
3 快取資料庫(用的最多,就是做快取)
4 釋出訂閱
5 去重
6 分散式(blpop)
# 7 安裝
-Redis-x64-3.2.100.msi 安裝包
-redis-desktop-manager-0.9.3.817.exe 等同於navicate
# 8 使用
-服務端和客戶端
-安裝完,服務端自動啟動:redis-server 配置檔案.conf
-redis-cli :客戶端連線服務端(redis-cli -h 127.0.0.1 -p 6379)