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
model

 

简单的登录页面

<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
View Code

 

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/',

    }

  },

 

posted @ 2018-07-26 18:04  R00M  阅读(357)  评论(0编辑  收藏  举报