基于Django rest framework 和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 importAPIViewfromrest_framework.response importResponseclassAuthView(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) 请求方法是以下三种方法之一:HEADGETPOST(2)HTTP的头信息不超出以下几种字段:AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-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 importAPIViewfromrest_framework.response importResponsefromdjango.shortcuts importHttpResponseclassAuthView(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 importMiddlewareMixinclassCORSMiddleware(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 importAPIViewfromrest_framework.response importResponsefromdjango.shortcuts importHttpResponsefromapi importmodelsimportuuid   # 网卡和时间生成的随机字符串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.$axiosVue.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 --saveVue.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 --saveVue.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.$axiosVue.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 importBaseAuthenticationfromrest_framework.exceptions importAuthenticationFailedfromapi importmodelsclassLuffyAuth(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 importLuffyAuthclassMicroView(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 --saveVue.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实现简单的在线教育平台的更多相关文章
- Django+xadmin打造在线教育平台(二)
		三.xadmin后台管理 3.1.xadmin的安装 django2.0的安装(源码安装方式): https://github.com/sshwsfc/xadmin/tree/django2 把zip ... 
- Django+xadmin打造在线教育平台(三)
		五.完成注册.找回密码和激活验证码功能 5.1.用户注册 register.html拷贝到templates目录 (1)users/views.py class RegisterView(View): ... 
- 第三百九十八节,Django+Xadmin打造上线标准的在线教育平台—生产环境部署CentOS6.5系统环境设置
		第三百九十八节,Django+Xadmin打造上线标准的在线教育平台—生产环境部署CentOS6.5系统环境设置 1.Linux安装配置 注意事项: 虚拟机网卡桥接模式 不要拨VPN 如果,网络怎么都 ... 
- 第三百九十五节,Django+Xadmin打造上线标准的在线教育平台—Xadmin集成富文本框
		第三百九十五节,Django+Xadmin打造上线标准的在线教育平台—Xadmin集成富文本框 首先安装DjangoUeditor3模块 Ueditor HTML编辑器是百度开源的HTML编辑器 下载 ... 
- 第三百八十四节,Django+Xadmin打造上线标准的在线教育平台—路由映射与静态文件配置以及会员注册
		第三百八十四节,Django+Xadmin打造上线标准的在线教育平台—路由映射与静态文件配置以及会员注册 基于类的路由映射 from django.conf.urls import url, incl ... 
- 第三百七十九节,Django+Xadmin打造上线标准的在线教育平台—xadmin的安装
		第三百七十九节,Django+Xadmin打造上线标准的在线教育平台—xadmin的安装 xadmin介绍 xadmin是基于Django的admin开发的更完善的后台管理系统,页面基于Bootstr ... 
- django+xadmin在线教育平台(五)
		3-3 django orm介绍与model设计 上节教程完成后代码(来学习本节前置条件): 对应commit: 留言板前端页面展示.本次内容截止教程3-2结束. 可能现在你还在通过手写sql语句来操 ... 
- django+xadmin在线教育平台(四)
		3-2 配置表单页面 必要的该说的,该了解的 前置条件: 你已经学习了前面教程.将项目的文件夹目录结构,setting配置等修改完毕与我保持一致. 本节通过Django快速的配置一个留言板页面来学习 ... 
- django+xadmin在线教育平台(一)
		大家好,此教程为在慕学网的实战教程Python升级3.6 强力Django+杀手级Xadmin打造在线教育平台的学习笔记,不对望指正! 使用Django+Xadmin打造在线教育平台(Python2, ... 
随机推荐
- [android] 在不同的activity之间传递数据
			新建一个activity,继承Activity 清单文件中进行配置,添加<activity/>节点 设置名称 android:name=”.类名” 点 代表的是当前包名,也可以不写 新建一 ... 
- idea护眼色设置
			idea右侧编辑区设置护眼色 
- 洛谷P4768 [NOI2018]归程(Kruskal重构树)
			题意 直接看题目吧,不好描述 Sol 考虑暴力做法 首先预处理出从$1$到每个节点的最短路, 对于每次询问,暴力的从这个点BFS,从能走到的点里面取$min$ 考虑如何优化,这里要用到Kruskal重 ... 
- zTree 节点文字过多处理方法
			zTree setting.view.addDiyDom 方法可以实现自定义控件,指定节点显示内容.因此需要自己实现addDiyDom方法. 如果树节点不显示checkbox ,处理方法为: func ... 
- Play 2D games on Nexus 6P running Android N7.1.1 with Daydream View VR headset
			http://files.cnblogs.com/files/we-hjb/N6P_Android7_SBS_SF.rar 
- Kotlin入门(25)共享参数模板
			共享参数SharedPreferences是Android最简单的数据存储方式,常用于存取“Key-Value”键值对数据.在使用共享参数之前,要先调用getSharedPreferences方法声明 ... 
- Ehcache缓存配置以及基本使用
			在java项目广泛的使用.它是一个开源的.设计于提高在数据从RDBMS中取出来的高花费.高延迟采取的一种缓存方案.正因为Ehcache具有健壮性(基于java开发).被认证(具有apache 2.0 ... 
- [Python][小知识][NO.3] Python 使用系统默认浏览器打开指定URL的网址
			1.前言 一般用到的地方: GUI交互界面下,单击某个按钮实现打开指定网址. 某帮助菜单项目,需要跳转网页显示时. O.O 某XX程序,需要植入网页弹窗广告时... 2.方法 调用 webbrowse ... 
- 单用户模式启动SQL Server实例总结
			在SQL Server的数据库维护过程中,有时候在一些特殊情况下需要在单用户模式下启动SQL Server实例. 下面总结一下单用户模式启动SQL Server的几种方式: 1:命令模式(sqls ... 
- 几种方法来实现scp拷贝时无需输入密码
			欢迎转载!转载时请注明出处:http://blog.csdn.net/nfer_zhuang/article/details/42646849 前言 我在工作中经常要将一些文件传输到另外一个服务器上, ... 
