基于Django rest framework 和Vue实现简单的在线教育平台

vue

 

一、基于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>

    exportdefault{

        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发送请求

                varthat = 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>

    exportdefault{

        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() {

                varnid = this.$route.params.id;    // 获取当前id值(用于拼接url)

                varthat = 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>

    exportdefault{

        name: "index",

        data() {

            return{

                detail: {     // 定义字典和相关的key

                    course: null,

                    img: null,

                    level: null,

                    slogon: null,

                    title: null,

                    why: null,

                    chapter: [],

                    recommends: [],

                }

            }

        },

        mounted() {

            varid = this.$route.params.id;    // 获取当前id值(用于拼接url)

            this.initDetail(id)

        },

        methods: {

            initDetail(nid) {

                varthat = 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 ###############

importLogin from \'../components/Login\'

Vue.use(Router);

 

exportdefaultnewRouter({

    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>

    exportdefault{

        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

fromrest_framework.views importAPIView

fromrest_framework.response importResponse

 

classAuthView(APIView):

    defpost(self, request, *args, **kwargs):

        print(request.data)

 

        returnResponse(\'...\')

(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

fromrest_framework.views importAPIView

fromrest_framework.response importResponse

fromdjango.shortcuts importHttpResponse

 

classAuthView(APIView):

 

    defoptions(self, request, *args, **kwargs):

        # 进行预检

        obj =HttpResponse(\'\')

        obj["Access-Control-Allow-Origin"] ="*"   # 允许你的域名来获取我的数据

        obj[\'Access-Control-Allow-Headers\'] ="Content-Type"  # 允许你携带Content-Type请求头

        returnobj

 

    defpost(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"] ="*"   # 允许你的域名来获取我的数据

 

        returnobj  # 返回值再加上一个响应头

  再次访问登录页面,尝试登录操作,可以看到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

fromdjango.utils.deprecation importMiddlewareMixin

 

classCORSMiddleware(MiddlewareMixin):

    """自定义中间件"""

 

    defprocess_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"

 

        ifrequest.method =="OPTIONS":

            response["Access-Control-Allow-Headers"] ="Content-Type"

        returnresponse

4、rest-framework登录验证

(1)给models.py添加User和Token模型

?

1

2

3

4

5

6

7

8

classUserInfo(models.Model):

    user =models.CharField(max_length=32)

    pwd =models.CharField(max_length=64)

 

 

classUserToken(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

fromrest_framework.views importAPIView

fromrest_framework.response importResponse

fromdjango.shortcuts importHttpResponse

fromapi importmodels

importuuid   # 网卡和时间生成的随机字符串

 

classAuthView(APIView):

 

    defpost(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()

        ifnotuser:

            ret[\'code\'] =1001

            ret[\'error\'] ="用户名或密码错误"

        else:

            uid =str(uuid.uuid4())  # 将生成的随机对象转化为随机字符串

            models.UserToken.objects.update_or_create(user=user, defaults={"token":uid})

            ret["token"] =uid

        returnResponse(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

importVue from\'vue\'

importVuex from\'vuex\'

//importCookie 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

importVue from\'vue\'

importApp from\'./App\'

importrouter from\'./router\'

importaxios from\'axios\'

importstore 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>

    exportdefault{

        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

importVue from \'vue\'

importVuex from \'vuex\'

importCookie from \'vue-cookies\'  // 引入cookie,npm install vue-cookies --save

 

Vue.use(Vuex);

 

exportdefaultnewVuex.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>

    exportdefault{

        data(){

            return{

                // 通过v-model双向绑定用户名和密码

                username:\'\',

                password:\'\'

            }

        },

        methods: {

            doLogin(){

                varthat = 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>

    exportdefault{

        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

importVue from \'vue\'

importVuex from \'vuex\'

importCookie from \'vue-cookies\'  // 引入cookie,npm install vue-cookies --save

 

Vue.use(Vuex);

 

exportdefaultnewVuex.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>

    exportdefault{

        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

exportdefaultnewRouter({

    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

importVue from \'vue\'

importApp from \'./App\'

importrouter from \'./router\'

importaxios from \'axios\'

importstore from \'./store/store\'

 

// 在vue的全局变量中设置了 $axios=axios

// 以后每个组件使用时:this.$axios

Vue.prototype.$axios = axios;

 

Vue.config.productionTip = false;

 

/* eslint-disable no-new */

newVue({

    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>

    exportdefault{

        data(){

            return{

                // 通过v-model双向绑定用户名和密码

                username:\'\',

                password:\'\'

            }

        },

        methods: {

            doLogin(){

                varthat = 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});

                        varurl = 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

classMicroView(APIView):

 

    defget(self, request, *args, **kwargs):

        token =request.query_params.get(\'token\')   # 获取到token

        obj =models.UserToken.objects.filter(token=token)   # 与数据库中token检验

        ifnotobj:

            returnResponse("认证失败")

        returnResponse("微职位")

(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>

    exportdefault{

        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

fromrest_framework.authentication importBaseAuthentication

fromrest_framework.exceptions importAuthenticationFailed

fromapi importmodels

 

classLuffyAuth(BaseAuthentication):

 

    defauthenticate(self, request):

        token =request.query_params.get("token")

        obj =models.UserToken.objects.filter(token=token).first()

        ifnotobj:

            raiseAuthenticationFailed({"code":1001, "error": "认证失败"})

        return(obj.user.user, obj)   # 返回用户名和token对象

(2)在MicroVIew视图类中添加认证组件 

?

1

2

3

4

5

6

7

8

9

fromapi.auth.auth importLuffyAuth

 

classMicroView(APIView):

 

    authentication_classes =[LuffyAuth]

 

    defget(self, request, *args, **kwargs):

        ret ={"code":1000, "title":"微职位"}

        returnResponse(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>

    exportdefault{

        name: "index",

        data() {

            return{

                title:null

            }

        },

        mounted(){   // 刚加载即执行

            this.initMicro()

        },

        methods:{

            initMicro(){

                varthat = 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

importVue from \'vue\'

importVuex from \'vuex\'

importCookie from \'vue-cookies\'  // 引入cookie,npm install vue-cookies --save

 

Vue.use(Vuex);

 

exportdefaultnewVuex.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,

以上是 基于Django rest framework 和Vue实现简单的在线教育平台 的全部内容, 来源链接: utcz.com/z/374653.html

回到顶部