1.概述
前文我们已经完成了登录页:
接下来我们要封装一下对token的操作和网络请求操作。之所以要封装这部分内容是因为token我们登陆后的所有请求都要携带,不可能每次都去重复的去手写:
token=localStorage.getToken('token') this.axios.post('接口API',参数+token) .then(res=>{ //业务逻辑 })
这样每次都重复的去手写明显不具有复用性,一旦token的存取逻辑或者API地址变了,到处都要去改。所以将他们抽出来封装是很有必要的。本文的主要内容就是讲解如何去封装token的操作和网络请求。
2.封装对token的操作
对一个应用系统来说,当用户登陆过后,后续的操作都是要进行权限校验的,也就是在请求后端接口的时候都要带上token。所以我们要将token存起来,首先封装一个对token的操作集合。在utils下面新建一个setToken的js:
setToken的内容很简单就是用localStorage来对token进行缓存:
export function setToken(tokenKey,token){ return localStorage.setItem(tokenKey,token) } export function getToken(tokenKey){ return localStorage.getItem(tokenKey) } export function removeToken(tokenKey){ return localStorage.removeItem(tokenKey) }
由于setToken只在登录的时候才需要用到,所以这里没必要将它引入到main.js中去让全局都能调用到,直接在Login组件中引入,在Login组件中能被调用就行了。这里我们换一种方式来引入,import的时候除了用相对路径和绝对路径来导入外,还可以用@来导入,@会自动帮我们定位到资源所在路径
import {setItem} from '@/utils/setToken.js'
Login组件:
<script> import {usernameAndPassWorldRule} from '../utils/validate.js' //引入想要的操作token的方法 import {setToken} from '@/utils/setToken.js' export default { data() { return { form: { username: "", password: "", }, rules:{ username:[{validator:usernameAndPassWorldRule,trigger:'blur'}], password:[{validator:usernameAndPassWorldRule,trigger:'blur'}] } }; }, methods:{ login(form){ this.$refs[form].validate((valid)=>{ if(valid){ console.log(this.form) this.$router.push('/home') this.axios.post('https://rapserver.sunmi.com/app/moc/340/login',this.form) .then(res=>{ console.log(res) if(res.data.status===200){ //使用引入的操作token的方法 setToken('username',res.data.username) this.$message({message:res.data.message,type:'success'}) this.$router.push('/home') } }) }else{ console.error(this.form) } }) } } }; </script>
ok,到了这一步我们已经完成了对token操作的封装,但是这就够了吗?很显然还不够的,至少还有两个地方还需要继续进行封装:
- 每次网络请求都要调用axios去写一大串地址,一旦地址变了,全局到处都要修改,很明显这里应该继续封装,免得搞得遍地都是。
- 后续的请求都要携带token,所以很显然还需要封装将token放到请求中去这个动作,免得搞得遍地都是。
这就是接下来我们要继续做的事儿——封装axios操作
3.封装axios
首先我们来解决前面提到的两点中的第一点:
每次网络请求都要调用axios去写一大串地址,一旦地址变了,全局到处都要修改,很明显这里应该继续封装,免得搞得遍地都是。
这个问题可以通过设置代理去解决,在vue.config.js中设置代理写好目标服务的URL前缀,顺便开启一下跨域:
module.exports = { devServer:{ open:true, host:'localhost', //配置代理 proxy:{ '/api':{ //目标地址 target:'http://127.0.0.1:8081/api/', //开启跨域 changeOrigin:true, pathRewrite:{ '^/api':'' } } } } }
接下来解决第二个问题:
后续的请求都要携带token,所以很显然还需要封装将token放到请求中去这个动作,免得搞得遍地都是。
封装一个axios的工具js,service.js:
service.js里面通过拦截器的方式来拦截request和response,在请求发起时放入token,在响应回来时处理报错:
import axios from "axios"; import { getToken } from "@/utils/setToken.js"; import { Message } from "element-ui"; const service= axios.create({ baseURL:'/api', timeout:3000 }) //添加请求拦截器 service.interceptors.request.use((config)=>{ //在请求之前做些什么(获取并设置token) config.headers['token']=getToken('token') return config },(error)=>{ return Promise.reject(error) }) //响应拦截器 service.interceptors.response.use((response)=>{ //对响应数据做些什么 let{status,message}=response.data if(status!=200){ //用elementUI的message来提升错误或者告警 Message({message:message||'error',type:'warning'}) } return response },(error)=>{ return Promise.reject(error) }) export default service
service.js是全局都要用到的,所以在main.js里引入:
import Vue from 'vue' import App from './App.vue' // import '../plugins/element.js' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import 'font-awesome/css/font-awesome.min.css' // import axios from 'axios' import router from './router' import service from './utils/service'; Vue.use(ElementUI) //挂载到原型,可以在全局使用 // Vue.prototype.axios=axios Vue.prototype.service=service; Vue.config.productionTip = false new Vue({ //挂在router router, render: h => h(App), }).$mount('#app')
最后来改一下Login组件,完整的用上setToken.js和service.js:
<template> <div class="login"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>通用后台管理系统</span> </div> <el-form label-width="80px" :model="form" ref="form" :rules="rules" > <el-form-item label="用户名" prop="username"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login('form')">登录</el-button> </el-form-item> </el-form> </el-card> </div> </template> <script> import {usernameAndPassWorldRule} from '../utils/validate.js' //引入想要的操作token的方法 import {setToken} from '@/utils/setToken.js' export default { data() { return { form: { username: "", password: "", }, rules:{ username:[{validator:usernameAndPassWorldRule,trigger:'blur'}], password:[{validator:usernameAndPassWorldRule,trigger:'blur'}] } }; }, methods:{ login(form){ this.$refs[form].validate((valid)=>{ if(valid){ console.log(this.form) this.service.post('/login',this.form) .then(res=>{ console.log(res.status) if(res.status===200){ setToken('username',res.data.username) setToken('token',res.data.token) this.$router.push('/home') } }) }else{ console.error(this.form) } }) } } }; </script> <style lang="less"> .login{ width: 100%; height:100%; position: absolute; background: #409EFF; .box-card{ width:450px; margin: 200px auto; .el-card__header{ font-size: 30px; } .el-button{ width: 100%; } } } </style>