一、基於api前端顯示課程詳細信息
1、調整Course.vue模塊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
<template>
<div>
<h1>課程列表</h1>
<div v-
for
=
"row in courseList"
>
<div style=
"width:350px;float: left;"
>
<!--<img src=
""
alt=
""
/>-->
<h3><router-link :to=
"{name:'detail', params:{id:row.id}}"
>{{row.title}}</router-link></h3>
<p>{{row.level}}</p>
</div>
</div>
</div>
</template>
<script>
export
default
{
name:
"index"
,
data() {
return
{
courseList: []
}
},
mounted:
function
() {
// vue頁面剛加載時自動執行
this
.initCourse()
},
methods: {
initCourse:
function
() {
/*
this.courseList = [
{id:1,title:'Python全棧'},
{id:2,title:'Linux運維'},
{id:3,title:'金融分析'},
]
*/
// 通過ajax向接口發送請求,並獲取課程列表
// axios 發送ajax請求
// npm install axios --save
// 第一步:在main.js中配置
// 第二步:使用axios發送請求
var
that =
this
;
this
.$axios.request({
url:
'http://127.0.0.1:8000/api/v1/course/'
,
method:
"GET"
}).then(
function
(ret) {
// ajax請求發送成功后,獲取的響應內容
console.log(ret.data);
if
(ret.data.code === 1000) {
// 注意這里的this已經不再是之前的this
that.courseList = ret.data.data
}
else
{
alert(
"獲取數據失敗"
);
}
}).
catch
(
function
(ret) {
// ajax請求失敗之后,獲取響應的內容
})
}
}
}
</script>
<style scoped>
</style>
|
顯示效果:
2、調整Detail.vue模塊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
<template>
<div>
<h1>課程詳細頁面</h1>
<div>
<p>{{detail.course}}</p>
<p>{{detail.img}}</p>
<p>{{detail.level}}</p>
<p>{{detail.slogon}}</p>
<p>{{detail.title}}</p>
<p>{{detail.why}}</p>
<div>
<ul v-
for
=
"item in detail.chapter"
>
<li>{{item.name}}</li>
</ul>
</div>
<div>
<ul v-
for
=
"item in detail.recommends"
>
<li>{{item.title}}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export
default
{
name:
"index"
,
data() {
return
{
detail: {
// 定義字典和相關的key
course:
null
,
img:
null
,
level:
null
,
slogon:
null
,
title:
null
,
why:
null
,
chapter: [],
recommends: [],
}
}
},
mounted() {
this
.initDetail()
},
methods: {
initDetail() {
var
nid =
this
.$route.params.id;
// 獲取當前id值(用於拼接url)
var
that =
this
;
this
.$axios.request({
// 發送axios請求
url:
'http://127.0.0.1:8000/api/v1/course/'
+ nid +
'/'
,
method:
'GET'
}).then(
function
(arg) {
// arg是返回的值:{code:1000, data:{...}}
// 將拿到的值賦值給detail
if
(arg.data.code === 1000) {
that.detail = arg.data.data
// 注意這里的this已經不是原來的this
}
else
{
alert(arg.data.error)
}
})
}
}
}
</script>
<style scoped>
</style>
|
顯示效果:
二、推薦課程切換及詳情展示
1、測試使用router-link是否合適
對Detail.vue修改如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<template>
<div>
<h1>課程詳細頁面</h1>
<div>
<p>{{detail.course}}</p>
<p>{{detail.img}}</p>
<p>{{detail.level}}</p>
<p>{{detail.slogon}}</p>
<p>{{detail.title}}</p>
<p>{{detail.why}}</p>
<div>
<ul v-
for
=
"item in detail.chapter"
>
<li>{{item.name}}</li>
</ul>
</div>
<div>
<h3>推薦課程</h3>
<ul v-
for
=
"item in detail.recommends"
>
<li><router-link :to=
"{name:'detail',params:{id:item.id}}"
>{{item.title}}</router-link></li>
</ul>
</div>
</div>
</div>
</template>
|
給推薦課程添加鏈接地址,點擊可以實現url切換,但是由於組件沒有重新加載,this.initDetail()沒有執行。
因此頁面的內容並不會發生切換。此方法不合適。
2、添加點擊事件處理推薦課程點擊切換
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
<template>
<div>
<h1>課程詳細頁面</h1>
<div>
<p>{{detail.course}}</p>
<p>{{detail.img}}</p>
<p>{{detail.level}}</p>
<p>{{detail.slogon}}</p>
<p>{{detail.title}}</p>
<p>{{detail.why}}</p>
<div>
<ul v-
for
=
"item in detail.chapter"
>
<li>{{item.name}}</li>
</ul>
</div>
<div>
<h3>推薦課程</h3>
<ul v-
for
=
"item in detail.recommends"
>
<!--為推薦課程添加點擊事件-->
<li @click=
"changeDetail(item.id)"
>{{item.title}}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export
default
{
name:
"index"
,
data() {
return
{
detail: {
// 定義字典和相關的key
course:
null
,
img:
null
,
level:
null
,
slogon:
null
,
title:
null
,
why:
null
,
chapter: [],
recommends: [],
}
}
},
mounted() {
var
id =
this
.$route.params.id;
// 獲取當前id值(用於拼接url)
this
.initDetail(id)
},
methods: {
initDetail(nid) {
var
that =
this
;
this
.$axios.request({
// 發送axios請求
url:
'http://127.0.0.1:8000/api/v1/course/'
+ nid +
'/'
,
method:
'GET'
}).then(
function
(arg) {
// arg是返回的值:{code:1000, data:{...}}
// 將拿到的值賦值給detail
if
(arg.data.code === 1000) {
that.detail = arg.data.data
// 注意這里的this已經不是原來的this
}
else
{
alert(arg.data.error)
}
})
},
changeDetail(id){
// click拿到課程id重新加載就可以渲染成功了
this
.initDetail(id);
// 切換頁面顯示
this
.$router.push({name:
'detail'
, params: {id:id}});
// 修改url地址
}
}
}
</script>
<style scoped>
</style>
|
注意:這里將var id = this.$route.params.id; 操作提到了vue生命周期mounted方法中。因此initDetail(nid)函數接收的nid,有可能是從mounted中傳遞過來的id也可以是changeDetail傳遞的id。
在 Vue 實例內部,你可以通過 $router
訪問路由實例。因此你可以調用 this.$router.push
。
1
|
this
.$router.push({name:
'detail'
, params: {id:id}});
// 命名的路由
|
顯示效果如下所示:
點擊推薦課程可以自由切換頁面路徑和頁面顯示。
三、用戶登錄功能實現
1、前端添加Login.vue模塊
(1)App.vue和index.js添加Login模塊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
############# App.vue ###############
<template>
<div id=
"app"
>
<router-link to=
"/index"
>首頁</router-link>
<router-link to=
"/course"
>課程</router-link>
<router-link to=
"/micro"
>微職位</router-link>
<router-link to=
"/news"
>深科技</router-link>
<div>
<router-link to=
"/login"
>登錄</router-link>
</div>
<router-view/>
</div>
</template>
############# index.js ###############
import
Login from
'../components/Login'
Vue.use(Router);
export
default
new
Router({
routes: [
// 其他代碼省略
{
path:
'/login'
,
name:
'login'
,
component: Login
},
]
})
|
(2)Login.vue構建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
<template>
<div>
<h2>用戶登錄</h2>
<div>
<p>
<input type=
"text"
placeholder=
"請輸入用戶名"
v-model=
"username"
>
</p>
<p>
<input type=
"password"
placeholder=
"請輸入密碼"
v-model=
"password"
>
</p>
<input type=
"button"
value=
"登錄"
@click=
"doLogin"
>
</div>
</div>
</template>
<script>
export
default
{
data(){
return
{
// 通過v-model雙向綁定用戶名和密碼
username:
''
,
password:
''
}
},
methods: {
doLogin(){
this
.$axios.request({
url:
'http://127.0.0.1:8000/api/v1/auth/'
,
method:
'POST'
,
data:{
user:
this
.username,
pwd:
this
.password
},
headers:{
'Content-Type'
:
'application/json'
}
}).then(
function
(arg) {
// 拿回結果
console.log(arg)
}).
catch
(
function
(arg) {
// 拿到錯誤信息
console.log(
"發生錯誤"
)
})
}
}
}
</script>
<style scoped>
</style>
|
注意:這里是通過v-model雙向綁定用戶名和密碼,並以此通過post請求來發送username和password。
2、django后台auth接口配置
(1)路由配置api/urls.py:
1
2
3
4
5
|
urlpatterns
=
[
"""代碼省略"""
url(r
'^(?P<version>[v1|v2]+)/auth/$'
, account.AuthView.as_view()),
]
|
(2)視圖配置api/view/account.py:
1
2
3
4
5
6
7
8
|
from
rest_framework.views
import
APIView
from
rest_framework.response
import
Response
class
AuthView(APIView):
def
post(
self
, request,
*
args,
*
*
kwargs):
print
(request.data)
return
Response(
'...'
)
|
(3)在前台頁面嘗試登陸
可以看到雖然配置的是post請求,但實際卻發送的是OPTIONS請求。
3、跨域問題處理
(1)簡單請求和非簡單請求
瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。
只要同時滿足以下兩大條件,就屬於簡單請求。
1
2
3
4
5
6
7
8
9
10
|
(1) 請求方法是以下三種方法之一:
HEAD
GET
POST
(2)HTTP的頭信息不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
|
凡是不同時滿足上面兩個條件,就屬於非簡單請求。
如果是復雜請求,會先用options請求進行預檢,通過之后才能發送post請求。
(2)配置修改account.py,添加options請求處理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
from
rest_framework.views
import
APIView
from
rest_framework.response
import
Response
from
django.shortcuts
import
HttpResponse
class
AuthView(APIView):
def
options(
self
, request,
*
args,
*
*
kwargs):
# 進行預檢
obj
=
HttpResponse('')
obj[
"Access-Control-Allow-Origin"
]
=
"*"
# 允許你的域名來獲取我的數據
obj[
'Access-Control-Allow-Headers'
]
=
"Content-Type"
# 允許你攜帶Content-Type請求頭
return
obj
def
post(
self
, request,
*
args,
*
*
kwargs):
print
(request.data)
# 同源策略禁止讀取位於 http://127.0.0.1:8000/api/v1/auth/ 的遠程資源。(原因:CORS 頭缺少 'Access-Control-Allow-Origin')
obj
=
Response(
"..."
)
obj[
"Access-Control-Allow-Origin"
]
=
"*"
# 允許你的域名來獲取我的數據
return
obj
# 返回值再加上一個響應頭
|
再次訪問登錄頁面,嘗試登錄操作,可以看到OPTIONS請求通過后,發送POST請求,python后端也打印出request.data中的數據。
(3)用中間件來處理跨域問題
上面這種方式過於麻煩了,一般還是交給中間件來處理跨域問題,為所有請求都設置頭。
/api/cors.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
from
django.utils.deprecation
import
MiddlewareMixin
class
CORSMiddleware(MiddlewareMixin):
"""自定義中間件"""
def
process_response(
self
, request, response):
# 添加響應頭
# 允許你的域名來獲取我的數據
response[
'Access-Control-Allow-Origin'
]
=
"*"
# 允許你攜帶Content-Type請求頭,這里不能寫*
# response['Access-Control-Allow-Headers'] = "Content-Type"
# 允許你發送GET/POST/DELETE/PUT
# response['Access-Control-Allow-Methods'] = "GET, POST"
if
request.method
=
=
"OPTIONS"
:
response[
"Access-Control-Allow-Headers"
]
=
"Content-Type"
return
response
|
4、rest-framework登錄驗證
(1)給models.py添加User和Token模型
1
2
3
4
5
6
7
8
|
class
UserInfo(models.Model):
user
=
models.CharField(max_length
=
32
)
pwd
=
models.CharField(max_length
=
64
)
class
UserToken(models.Model):
user
=
models.OneToOneField(to
=
"UserInfo"
, on_delete
=
models.CASCADE)
token
=
models.CharField(max_length
=
64
)
# 不僅可以配置token,還可以配置超時時間
|
利用makemigrations和migrate完成數據遷移操作。在UserInfo表添加用戶和密碼。
(2)后端處理登錄信息,更新並創建token信息
重寫/api/views/account.py如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
from
rest_framework.views
import
APIView
from
rest_framework.response
import
Response
from
django.shortcuts
import
HttpResponse
from
api
import
models
import
uuid
# 網卡和時間生成的隨機字符串
class
AuthView(APIView):
def
post(
self
, request,
*
args,
*
*
kwargs):
"""
用戶登錄認證
:param request:
:param args:
:param kwargs:
:return:
"""
print
(request.data)
ret
=
{
'code'
:
1000
}
# 用get方法取的話,不存在即為Null
user
=
request.data.get(
"user"
)
pwd
=
request.data.get(
"pwd"
)
user
=
models.UserInfo.objects.
filter
(user
=
user, pwd
=
pwd).first()
if
not
user:
ret[
'code'
]
=
1001
ret[
'error'
]
=
"用戶名或密碼錯誤"
else
:
uid
=
str
(uuid.uuid4())
# 將生成的隨機對象轉化為隨機字符串
models.UserToken.objects.update_or_create(user
=
user, defaults
=
{
"token"
:uid})
ret[
"token"
]
=
uid
return
Response(ret)
|
(3)登錄驗證
在vue前端登錄,顯示信息如下:
在python后台打印request.data信息:{'user': 'asdw', 'pwd': 'asdw131'}、{'user': 'oldboy', 'pwd': '123'}。
5、用vuex實現在各個組件中共享值
(1)全局變量配置
1)創建/src/store文件夾,創建並編寫store.js文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import
Vue
from
'vue'
import
Vuex
from
'vuex'
/
/
import
Cookie
from
'vue-cookies'
Vue.use(Vuex)
export default new Vuex.Store({
/
/
組件中通過 this.$store.state.username 調用
state: {
username: null,
token: null,
},
})
|
組件中通過 this.$store.state.username 調用。
2)在main.js中引入store,並放入實例化組件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import
Vue
from
'vue'
import
App
from
'./App'
import
router
from
'./router'
import
axios
from
'axios'
import
store
from
'./store/store'
/
/
在vue的全局變量中設置了 $axios
=
axios
/
/
以后每個組件使用時:this.$axios
Vue.prototype.$axios
=
axios;
Vue.config.productionTip
=
false;
/
*
eslint
-
disable no
-
new
*
/
new Vue({
el:
'#app'
,
router,
store,
/
/
放入實例化中
components: {App},
template:
'<App/>'
})
|
(2)在所有組件中使用全局變量
Login.vue:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
<script>
export default {
data(){
return
{
/
/
通過v
-
model雙向綁定用戶名和密碼
username:'',
password:''
}
},
methods: {
doLogin(){
var that
=
this;
this.$axios.request({
url:
'http://127.0.0.1:8000/api/v1/auth/'
,
method:
'POST'
,
data:{
user:this.username,
pwd:this.password
},
headers:{
'Content-Type'
:
'application/json'
}
}).then(function (arg) {
/
/
拿回結果
if
(arg.data.code
=
=
=
1000
){
/
/
成功的情況下
that.$store.state.token
=
arg.data.token;
that.$store.state.username
=
that.username;
}
else
{
alert(arg.data.error)
}
}).catch(function (arg) {
/
/
拿到錯誤信息
console.log(
"發生錯誤"
)
})
}
}
}
<
/
script>
|
App.vue:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<template>
<div id=
"app"
>
<router-link to=
"/index"
>首頁</router-link>
<router-link to=
"/course"
>課程</router-link>
<router-link to=
"/micro"
>微職位</router-link>
<router-link to=
"/news"
>深科技</router-link>
<div v-
if
=
"this.$store.state.token"
>
<a href=
""
>{{
this
.$store.state.username}}</a>
</div>
<div v-
else
>
<router-link to=
"/login"
>登錄</router-link>
</div>
<router-view/>
</div>
</template>
<script>
export
default
{
name:
'App'
}
</script>
|
如此就可以通過獲取全局變量實現用戶登錄效果:
但是這種登錄狀態,只要瀏覽器一刷新,登錄狀態就消失了,因此登錄成功不僅要設置到全局變量,還要在cookie中放一份全局變量。
6、vue-cookies應用
(1)store.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import
Vue from
'vue'
import
Vuex from
'vuex'
import
Cookie from
'vue-cookies'
// 引入cookie,npm install vue-cookies --save
Vue.use(Vuex);
export
default
new
Vuex.Store({
// 組件中通過 this.$store.state.username 調用
state: {
// 默認去cookie中取值
username: Cookie.get(
"username"
),
token: Cookie.get(
"token"
),
},
mutations: {
// 組件中通過this.$store.commit(函數名, 參數)調用
saveToken:
function
(state, userToken) {
state.username = userToken.username;
state.token = userToken.token;
Cookie.set(
"username"
, userToken.username,
"20min"
);
Cookie.set(
"token"
, userToken.token,
"20min"
);
},
}
})
|
1)注意引入cookie的方法;
2)注意mutations方法。更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。
3)組件中通過this.$store.commit(函數名, 參數)調用。
(2)Login.vue修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
<script>
export
default
{
data(){
return
{
// 通過v-model雙向綁定用戶名和密碼
username:
''
,
password:
''
}
},
methods: {
doLogin(){
var
that =
this
;
this
.$axios.request({
url:
'http://127.0.0.1:8000/api/v1/auth/'
,
method:
'POST'
,
data:{
user:
this
.username,
pwd:
this
.password
},
headers:{
'Content-Type'
:
'application/json'
}
}).then(
function
(arg) {
// 拿回結果
if
(arg.data.code === 1000){
// 成功的情況下
// that.$store.state.token = arg.data.token;
// that.$store.state.username = that.username;
that.$store.commit(
'saveToken'
,{token: arg.data.token, username: that.username});
}
else
{
alert(arg.data.error)
}
}).
catch
(
function
(arg) {
// 拿到錯誤信息
console.log(
"發生錯誤"
)
})
}
}
}
</script>
|
(3)刷新仍在全局顯示登錄用戶
(4)添加登出注銷操作
App.vue:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<template>
<div id=
"app"
>
<router-link to=
"/index"
>首頁</router-link>
<router-link to=
"/course"
>課程</router-link>
<router-link to=
"/micro"
>微職位</router-link>
<router-link to=
"/news"
>深科技</router-link>
<div v-
if
=
"this.$store.state.token"
>
<a href=
""
>{{
this
.$store.state.username}}</a>
<a @click=
"logout"
>注銷</a>
</div>
<div v-
else
>
<router-link to=
"/login"
>登錄</router-link>
</div>
<router-view/>
</div>
</template>
<script>
export
default
{
name:
'App'
,
methods:{
logout(){
// 注銷
this
.$store.commit(
'clearToken'
);
}
}
}
</script>
|
store.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import
Vue from
'vue'
import
Vuex from
'vuex'
import
Cookie from
'vue-cookies'
// 引入cookie,npm install vue-cookies --save
Vue.use(Vuex);
export
default
new
Vuex.Store({
// 組件中通過 this.$store.state.username 調用
state: {
// 默認去cookie中取值
username: Cookie.get(
"username"
),
token: Cookie.get(
"token"
),
},
mutations: {
// 組件中通過this.$store.commit(函數名, 參數)調用
saveToken:
function
(state, userToken) {
state.username = userToken.username;
state.token = userToken.token;
Cookie.set(
"username"
, userToken.username,
"20min"
);
Cookie.set(
"token"
, userToken.token,
"20min"
);
},
clearToken:
function
(state) {
state.username =
null
;
state.token =
null
;
Cookie.remove(
"username"
);
Cookie.remove(
"token"
);
}
}
})
|
登出效果如下所示:
點擊注銷后顯示效果:
四、攔截器
有些頁面登錄了才能訪問,有些頁面不需要登錄即可訪問。
1、頁面訪問登錄判斷
這里以micro模塊為例,給模塊添加登錄判斷,用戶未登錄時訪問微職業,直接跳轉到登錄頁面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<template>
<div>
<h1>LuffyX學位</h1>
</div>
</template>
<script>
export
default
{
name:
"index"
,
data() {
return
{
}
},
mounted(){
// 剛加載即執行
if
(!
this
.$store.state.token){
// 重定向返回登錄頁面
this
.$router.push({name:
"login"
})
}
}
}
</script>
<style scoped>
</style>
|
但是對於組件很多的網站卻不能這么處理,而是應該使用vue自帶的攔截器來處理。
2、添加攔截器
(1)在路由控制中給需要攔截的路由配置meta字段
index.js:給需要攔截的路由配置meta字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
export
default
new
Router({
routes: [
{
path:
'/index'
,
name:
'index'
,
component: Index,
},
{
path:
'/course'
,
name:
'course'
,
component: Course
},
{
path:
'/detail/:id'
,
// 動態接收名字為id的值
name:
'detail'
,
component: Detail
},
{
path:
'/micro'
,
name:
'micro'
,
component: Micro,
meta:{
requireAuth:
true
// 表示必須要登錄
}
},
{
path:
'/news'
,
name:
'news'
,
component: News,
meta:{
requireAuth:
true
// 表示必須要登錄
}
},
{
path:
'/login'
,
name:
'login'
,
component: Login
},
],
mode:
'history'
})
|
(2)添加配置攔截器
main.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
import
Vue from
'vue'
import
App from
'./App'
import
router from
'./router'
import
axios from
'axios'
import
store from
'./store/store'
// 在vue的全局變量中設置了 $axios=axios
// 以后每個組件使用時:this.$axios
Vue.prototype.$axios = axios;
Vue.config.productionTip =
false
;
/* eslint-disable no-new */
new
Vue({
el:
'#app'
,
router,
store,
// 放入實例化中
components: {App},
template:
'<App/>'
});
// 攔截器 to:要去哪 next:去跳轉 from:從哪來
router.beforeEach(
function
(to, from, next) {
if
(to.meta.requireAuth) {
// 當前要去的url只有登錄后才能訪問
if
(store.state.token) {
// token為true表示可以繼續訪問
next()
}
else
{
// token不為true跳轉到登錄頁面
next({path:
'/login'
,})
}
}
else
{
// url不需要訪問即可以訪問
next()
}
});
|
3、登錄后直接顯示登錄前頁面
比如在訪問微職業時,由於沒有登錄跳轉到了登錄頁面,輸入賬戶密碼登錄后,顯示的內容應該是微職業的內容。
(1)修改main.js中的攔截器
在url地址中添加返回的url:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 攔截器 to:要去哪 next:去跳轉 from:從哪來
router.beforeEach(
function
(to, from, next) {
if
(to.meta.requireAuth) {
// 當前要去的url只有登錄后才能訪問
if
(store.state.token) {
// token為true表示可以繼續訪問
next()
}
else
{
// token不為true跳轉到登錄頁面
next({path:
'/login'
, query:{backUrl: to.fullPath}})
}
}
else
{
// url不需要訪問即可以訪問
next()
}
});
|
(2)Login.vue中修改登錄操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
<script>
export
default
{
data(){
return
{
// 通過v-model雙向綁定用戶名和密碼
username:
''
,
password:
''
}
},
methods: {
doLogin(){
var
that =
this
;
this
.$axios.request({
url:
'http://127.0.0.1:8000/api/v1/auth/'
,
method:
'POST'
,
data:{
user:
this
.username,
pwd:
this
.password
},
headers:{
'Content-Type'
:
'application/json'
}
}).then(
function
(arg) {
// 拿回結果
if
(arg.data.code === 1000){
// 成功的情況下
that.$store.commit(
'saveToken'
,{token: arg.data.token, username: that.username});
var
url = that.$route.query.backUrl;
if
(url) {
that.$router.push({path:url})
}
else
{
that.$router.push({path:
'/index'
})
}
}
else
{
alert(arg.data.error)
}
}).
catch
(
function
(arg) {
// 拿到錯誤信息
console.log(
"發生錯誤"
)
})
}
}
}
</script>
|
(3)登錄驗證
登錄成功后顯示效果:
五、用戶認證
1、通過token進行用戶認證
(1)配置micro的url和視圖
api/urls.py:
1
2
3
4
|
urlpatterns = [
""
"省略"
""
url(r
'^(?P<version>[v1|v2]+)/micro/$'
, course.MicroView.as_view()),
]
|
Couse.py添加MicroView視圖:
1
2
3
4
5
6
7
8
|
class
MicroView(APIView):
def
get(
self
, request,
*
args,
*
*
kwargs):
token
=
request.query_params.get(
'token'
)
# 獲取到token
obj
=
models.UserToken.objects.
filter
(token
=
token)
# 與數據庫中token檢驗
if
not
obj:
return
Response(
"認證失敗"
)
return
Response(
"微職位"
)
|
(2)配置Micro.vue向后端發送GET請求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<script>
export
default
{
name:
"index"
,
data() {
return
{
title:
null
}
},
mounted(){
// 剛加載即執行
this
.initMicro()
},
methods:{
initMicro(){
this
.$axios.request({
url:
'http://127.0.0.1:8000/api/v1/micro/'
, // 這個地址如果被盜,任何人都可以獲取數據
method:
"GET"
,
params:{
token:
this
.$store.state.token
}
}).then(
function
(arg) {
console.log(arg);
})
}
}
}
</script>
|
這里需要注意不能只配置Url,這個地址如果被盜,則任何人都可以向后端發送請求獲取數據。
因此配置params參數,在url地址后拼接token參數來發送請求:
(3)django訪問檢驗
當token不正確時:
2、通過rest認證組件實現用戶認證
(1) 在應用api下添加文件夾auth,添加auth.py文件
1
2
3
4
5
6
7
8
9
10
11
12
|
from
rest_framework.authentication
import
BaseAuthentication
from
rest_framework.exceptions
import
AuthenticationFailed
from
api
import
models
class
LuffyAuth(BaseAuthentication):
def
authenticate(
self
, request):
token
=
request.query_params.get(
"token"
)
obj
=
models.UserToken.objects.
filter
(token
=
token).first()
if
not
obj:
raise
AuthenticationFailed({
"code"
:
1001
,
"error"
:
"認證失敗"
})
return
(obj.user.user, obj)
# 返回用戶名和token對象
|
(2)在MicroVIew視圖類中添加認證組件
1
2
3
4
5
6
7
8
9
|
from
api.auth.auth
import
LuffyAuth
class
MicroView(APIView):
authentication_classes
=
[LuffyAuth]
def
get(
self
, request,
*
args,
*
*
kwargs):
ret
=
{
"code"
:
1000
,
"title"
:
"微職位"
}
return
Response(ret)
|
訪問django頁面驗證:
(3)前端vue處理后端返回的數據
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
<template>
<div>
<h1>LuffyX學位:{{title}}</h1>
</div>
</template>
<script>
export
default
{
name:
"index"
,
data() {
return
{
title:
null
}
},
mounted(){
// 剛加載即執行
this
.initMicro()
},
methods:{
initMicro(){
var
that =
this
;
this
.$axios.request({
url:
'http://127.0.0.1:8000/api/v1/micro/'
, // 這個地址如果被盜,任何人都可以獲取數據
method:
"GET"
,
params:{
token:
this
.$store.state.token
}
}).then(
function
(arg) {
if
(arg.data.code === 1000) {
that.title = arg.data.title
}
})
}
}
}
</script>
|
訪問http://localhost:8080/micro,效果如下所示:
六、vue接口歸總
1、在vuex中設置apiList字段歸總所有rest接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import
Vue from
'vue'
import
Vuex from
'vuex'
import
Cookie from
'vue-cookies'
// 引入cookie,npm install vue-cookies --save
Vue.use(Vuex);
export
default
new
Vuex.Store({
// 組件中通過 this.$store.state.username 調用
state: {
// 默認去cookie中取值
username: Cookie.get(
"username"
),
token: Cookie.get(
"token"
),
apiList: {
// 所有的接口
course:
'http://127.0.0.1:8000/api/v1/course/'
,
courseDetail:
'http://127.0.0.1:8000/api/v1/course/'
,
auth:
'http://127.0.0.1:8000/api/v1/auth/'
,
micro:
"http://127.0.0.1:8000/api/v1/micro/"
,
}
},
mutations: {
/* 代碼省略*/
}
})
|
2、替換各個模塊中的url地址
均按照如下方法替換:
1
2
3
4
|
url:
this
.store.state.apiList.micro,
url:
this
.store.state.apiList.course,
url:
this
.store.state.apiList.course + nid +
'/'
,
url:
this
.store.state.apiList.auth,
|