路飞学城前后端分离项目2
推荐课程的开发
需求:
我们在点击推荐课程的时候,会有该推荐课程的详细页面
思路:
为每一个推荐课程标签添加事件,重新向后台请求数据加载页面,但是url不会发生改变
使用router-link,因为都在在detail路由中,页面不会重新加载,所以只要url改变,页面不改变
需要说明的是,vue跟jquery不同,因为vue是单页面,所有他做的只是页面组件的重建与销毁
如若你只是改变路由的话,那么页面不会有变化.
如果你只是改变组件那么路由不会发生变化.
我们开发的时候需要两个一起改变
<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>-->
<li @click="changeDetail(item.id)">{{item.title}}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: "index",
data() {
return {
detail:{
course:null,
img:null,
level:null,
slogon:null,
title:null,
why:null,
chapter:[],
recommends:[],
}
}
},
mounted(){
var id = this.$route.params.id
this.initDetail(id)
},
methods:{
initDetail(nid){
var that = this
this.$axios.request({
url:'http://127.0.0.1:8000/api/v1/course/'+ nid +'/',
method:'GET'
}).then(function (arg) {
if(arg.data.code === 1000){
that.detail = arg.data.data
}else{
alert(arg.data.error)
}
})
},
changeDetail(id){
this.initDetail(id)
this.$router.push({name:'detail',params:{id:id}})
}
}
}
</script>
<style scoped>
</style>
vue中跳转路径方法补充:
this.$router.push({name:'detail',params:{id:id}})
用户登录
需求:
- 实现用户的登录并放置vue-cookies中
- 并且随机生成token,访问需要登录的页面都带着token值
思路:
- 用户登录的时候先在前端获取用户名密码、如果有token值,生成token,如果没有则创建
- 将token值和nikname返回给前端
- 前端在vue-cookies保存token值和nikname作持久化,
- 登录成功则显示nikname和注销
前端登录页面
Login.vue
<template>
<div>
<h1>用户登录</h1>
<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 {
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})
}else{
alert(arg.data.error)
}
}).catch(function (arg) {
console.log('发生错误')
})
}
}
}
</script>
<style scoped>
</style>
store目录下的store.vue
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: Cookie.get("username"), #store每次都从cookie中取值
token:Cookie.get("token"),
},
mutations: {
// 组件中通过 this.$store.commit(saveToken,参数) 调用
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')
}
}
})
在store中,使用vue设置全局变量,vue-cookies保存全局变量,使其刷新后仍然存在于vue-cookies中
vuex全局变量的用法
1、创建store.js
2、main.js中
import store from './store/store' 放到实例化里边
组建中可以通过this.$store.state.username调用
vue-cookies的下载和使用
下载
npm install vue-cookis --save
使用:
如果登录成功,调用store的mutations方法:

关于向后台发送数据的简单请求和复杂请求
条件:
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请求过来后,
给他加上一个特殊的请求头,那么就可以通过了
class CORSMiddleware(MiddlewareMixin):
def process_response(self,request,response):
# 添加响应头
# 允许你的域名来获取我的数据
# response['Access-Control-Allow-Origin'] = "*"
# 允许你携带Content-Type请求头
# response['Access-Control-Allow-Headers'] = "Content-Type"
# 允许你发送DELETE,PUT
# response['Access-Control-Allow-Methods'] = "DELETE,PUT"
response['Access-Control-Allow-Origin'] = "*"
if request.method == "OPTIONS":
response['Access-Control-Allow-Headers'] = "Content-Type"
response['Access-Control-Allow-Methods'] = "PUT,DELETE"
return response
使用中间件为每次请求添加响应头
后端登录操作
models.py中
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
url.py
url(r'^auth/$', account.AuthView.as_view()),
view.py中
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: """ ret = {'code':1000} 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)
vue-拦截器
用拦截器实现的功能:
在需要登录才能访问的页面,如果未登录访问就拦截下来,跳转到登录页面
登录成功后再跳到访问的页面
1、在路径中添加 meta
{
path: '/news',
name: 'news',
component: News,
meta:{
requireAuth:true
}
},
2、main.js中
router.beforeEach(function (to, from, next) {
if(to.meta.requireAuth){
// 要去的url只有登陆成功后才能访问
if (store.state.token) {
next() #next表示去访问,本来是去哪访问就去哪访问
} else {
next({name: 'login',query: {backUrl: to.fullPath}})
}
}else{
next()
}
})
3、login中做判断,跳转url
}).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'})
}
后端认证
用户获取到后端接口,直接不登录访问后端的接口,
如果后端没有认证的话,那么接口的内容就会被暴露
Micro.vue
<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>
<style scoped>
</style>
url.py
url(r'^micro/$', course.MicroView.as_view()),
auth认证
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)
view.py中使用
class MicroView(APIView):
authentication_classes = [LuffyAuth,]
def get(self,request,*args,**kwargs):
ret = {'code':1000,'title':'微职位'}
return Response(ret)
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/',
}
},
