Django 路飞学城前后端分离2
推荐课程的开发
需求:
我们在点击推荐课程的时候,会有该推荐课程的详细页面
思路:
- 为每一个推荐课程标签添加事件,重新加载页面
- 同时加载成功后路由发生相应的变化
需要说明的是,vue跟jquery不同,因为vue是单页面,所有他做的只是页面组件的重建与销毁
如若你只是改变路由的话,那么页面不会有变化.
如果你只是改变组件那么路由不会发生变化.
我们开发的时候需要两个一起改变
添加事件
<h2>推荐课程</h2> <ul v-for="item in detailList.recommend_course"> <li @click="changeDetail(item.id)">{{item.title}}--->{{item.level}}</li> </ul>
获取数据
methods:{ //发送axios请求获取数据 initDetail(nid){ let that=this; this.$axios.request({ url:`http://127.0.0.1:8000/api/v2/course/${nid}/`, method:'GET', }).then(function(ret){ //获取成功后的回调方法 if (ret.data.code === 1000){ //此this非彼this 所以用that that.detailList=ret.data.data; } }).catch(function(ret){ })}, changeDetail(id){ this.initDetail(id); this.$router.push({name:'detail',params:{id:id}}) }, }, created(){ }, mounted(){ //页面加载的时候触发 let nid=this.$route.params.id; this.initDetail(nid)
跳转路径方法补充:
this.$router.push({name:'detail',params:{id:id}})
那么push后面也可以接受path参数,{path:'/index'} 就会跳转到相应的视图
用户登录
需求:
- 实现用户的登录并放置vue-cookies中
- 并且随机生成token,访问需要登录的页面都带着token值
思路:
- 用户登录的时候先在前端获取用户名密码、如果有token值,生成token,如果没有则创建
- 将token值和nikname返回给前端
- 前端在vue-cookies保存token值和nikname作持久化,
- 登录成功则显示nikname和注销
需要登录,那么表结构必须有用户名密码,和token表
class UserInfo(models.Model): '''用户表''' username=models.CharField(verbose_name='用户名',max_length=32) password=models.CharField(verbose_name='密码',max_length=64) nikname=models.CharField(max_length=32,verbose_name='昵称') def __str__(self): return self.nikname class Token(models.Model): user=models.OneToOneField('UserInfo',on_delete=models.CASCADE,verbose_name='用户') token=models.CharField(max_length=128) def __str__(self): return self.user
简单的登录页面
<template> <div> <h1>登录页面</h1> <p> <input type="text" placeholder="用户名" v-model="username"> </p> <p> <input type="password" placeholder="密码" v-model="pwd"> </p> <button @click="dologin">登录</button> </div> </template>
<script> export default { name: "Vlogin", data() { return { username:'', pwd:'', } }, methods:{ dologin(){ let that=this; this.$axios.request({ url:'http://127.0.0.1:8000/api/v2/login/', method:'POST', headers:{'Content-Type':'application/json'}, data:{username:this.username,pwd:this.pwd} }).then(function (arg) {} </script>
如果这里进行发送的时候就涉及到复杂的请求的跨域
CORS之复杂请求
条件:
1、请求方式:HEAD、GET、POST
2、请求头信息:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type 对应的值是以下三个中的任意一个
application/x-www-form-urlencoded
multipart/form-data
text/plain
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求
复杂请求处理
如果是复杂请求,那么就会先发一个options请求到服务里进行'预检'
询问服务器是否允许我用post请求进行访问.如果是options请求过来后,
给他加上一个特殊的请求头,那么就可以通过了
# 允许你携带Content-Type请求头 如果要多的用逗号,隔开 if request.method == 'OPTIONS': response['Access-Control-Allow-Headers']='Content-Type'
后端登录业务逻辑
from rest_framework.views import APIView from rest_framework.response import Response from api import models import uuid class LoginView(APIView): def post(self,request,*args,**kwargs): ret={'code':1000,'data':None} try: username=request.data.get('username') pwd = request.data.get('pwd') user=models.UserInfo.objects.filter(username=username,password=pwd).first() if user: uu=uuid.uuid4() models.Token.objects.update_or_create(user=user,defaults={'token':uu}) ret['token']=uu ret['data']={'nikname':user.nikname} except Exception as e: ret['code']=1001 ret['error']='用户名或密码错误' return Response(ret)
接下来就是将返回的token和nikname放置在vue-cookies:
vue-cookies的下载和使用
下载vue-cookies
npm install vue-cookis --save
使用:
如果登录成功,调用store的mutations方法:
if(arg.data.code === 1000){ that.$store.commit('SAEVTOKEN',{nikname:arg.data.data.nikname,token:arg.data.token});}
import Cookie from 'vue-cookies'
mutations: { SAEVTOKEN(state,newValue){ state.token =newValue.token; state.nikname=newValue.nikname; Cookie.set('token', state.token,'20min'); Cookie.set('nikname', state.nikname,'20min'); },
#注销 CLEARTOKEN(state){ state.token =null; state.nikname=null; Cookie.remove('token'); Cookie.remove('nikname'); }}
<span v-if="this.$store.state.nikname"> <a >{{this.$store.state.nikname}}</a> <a @click="logout">注销</a> </span>
登录后发现刷新页面后,cookies也不生效了
其实是一开始就要从cookies里边取值
const store = new Vuex.Store({
state: {
nikname:Cookie.get('nikname'),
token:Cookie.get('token'),
},
vue-拦截器
用拦截器实现的功能:
在需要登录才能访问的页面,如果未登录访问就拦截下来
跳转到登录页面
实现拦截和页面跳转
index.js:
{
path: '/micro',
name: 'micro',
component: Vmicro,
meta:{requireAuth:true}
},
在路由里添加meta,标记上哪些路由需要登录才能访问
在main.js:
router.beforeEach(function (to,from,next) {
if(to.meta.requireAuth){
if(store.state.token){
next()
}else {
next({name:'login',query:{backUrl:to.fullPath}})
}
}
else {
next()
}
});
如果有token值就直接跳过,
如果需要登录没有token值就需要跳转到login
backUrl里的to.fullPath就是访问的url
login.vue里
if(arg.data.code === 1000){
that.$store.commit('SAEVTOKEN',{nikname:arg.data.data.nikname,token:arg.data.token});
let url=that.$route.query.backUrl;
if(url){
that.$router.push({path:url})
}
else{
that.$router.push({path:'/home'})
}
}
url=that.$route.query.backUrl;可以获取到backurl
如果有登录成功后则跳转到访问的页面,没有则调回到主页
后端认证
用户获取到后端接口,直接不登录访问后端的接口,
如果后端没有认证的话,那么接口的内容就会被暴露
from rest_framework.authentication import BaseAuthentication from api import models from rest_framework.exceptions import AuthenticationFailed class AuthLogin(BaseAuthentication): def authenticate(self, request): token=request.GET.get('token') obj=models.Token.objects.filter(token=token).first() if not obj: raise AuthenticationFailed('认证失败') return obj.user.username,obj
API的统一存放
当你写了很多API后,你就会发现你自己不知道写了哪些路由,
那么如果有人问你要接口的时候你可能会找很久.那么我们将api的路由
统一放到vue的store里
放在api的store里有什么好处?
- 查找的时候变的方便,只要在store里查找就可以
- 发送axios请求的时候可以在store里获取
const store = new Vuex.Store({
state: {
apiList:{
Course:'http://127.0.0.1:8000/api/v2/course/',
CourseDetail:'http://127.0.0.1:8000/api/v2/course/',
Login:'http://127.0.0.1:8000/api/v2/login/',
Micro:'http://127.0.0.1:8000/api/v2/micro/',
}
},