前端时间项目需要发布一个较大的版本,工作比较忙,加了好多个晚上的班,感觉自己有点缺氧了。最近稍微闲下来了,顺便调休了三天,刚刚给家里来了个大扫除,看着这干干净净的小家,心里顿时舒服了很多。
下面进入正题(本文以vue项目为例)→
- 首先,安装axios:
npm install axios
- 然后在 main.js 里引入 axios,然后进行全局挂载在vue原型上
import axios from 'axios';
Vue.prototype.$axios = axios;
使用方式:
this.$axios.post(api, this.form).then(res => {console.log(111, res)})
当然,这里还有另外一种方式让我们可以在全局直接使用axios
- 首先,安装axios, vue-axios:
npm install --save axios vue-axios
- 然后在 main.js 里引入 axios,然后使用 vueAxios进行全局挂载
import vueAxios from 'vue-axios'
Vue.use(vueAxios, axios);
使用方式:
Vue.axios.get(api).then((response) => {console.log(response.data)
})this.axios.get(api).then((response) => {console.log(response.data)
})this.$http.get(api).then((response) => {console.log(response.data)
})
跨域问题:
在vue或者react项目中,我们大多使用代理的方式来解决跨域问题
- 首先,找到 config文件下的 index.js 文件,在proxyTable下加上以下代码
proxyTable: {'/apis': {// 测试环境target: 'http://192.168.0.113', // 后端接口域名changeOrigin: true, //是否跨域pathRewrite: {'^/apis': '' //需要rewrite重写的,}}},
*tips:因为我们修改了配置文件,所以在这一步之后需要我们重启项目才可生效
接下来就是发送请求,可能我们对于上面刚加的代码不是很理解,比如说:
这个 '^/apis ’ 是什么意思?
我们仔细一看,他就像是一个正则表达式:^ 符号代表以什么开头,这里就是以 /apis 开头,当我们使用axios发送请求时:
他就会检测到这里的请求 api 为: ‘/apis/login’ ,是以 /apis 开头的路径,那他就会将我们的请求前缀替换成为 ‘http://192.168.0.113’
http://localhost:8888/apis/login
等同于:
http://192.168.0.113/login
路由拦截
我们的项目中很多页面是需要进行登录才能浏览的,但是如果我们每进入一个页面都去判断他是否有登录(Token),明显不是一个明智之举,所以我们需要一个公共的方法去处理这件事情。
以 vue-router 为例,我们知道 vue-router 具有两个全局守卫:
全局前置守卫:router.beforeEach((to, from, next) => {// ...
})to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。全局后置钩子:
router.afterEach((to, from) => {// ...
})
在这里,我们正是要用到 router.beforeEach 这个钩子函数
让我们来打印康康 to 这个参数有什么东西:
其中里面有一个 mata 的对象,他代表了什么呐?
没错,正是它。我们给路径为 ‘/’ 的这个路由配置一个参数requireAuth
// 需要登录才能进入的页面可以增加一个meta属性
meta: { requireAuth: true
},
这样,我们就可以 to 这个参数里找到他了
相信讲到这里,你一定有了自己的实现思路了
// 判断是否需要登录权限 以及是否登录
router.beforeEach((to, from, next) => {console.log('to', to)const { meta } = to || {}const { requireAuth } = meta || {}// 即将进入的页面是否需要登录if(requireAuth) {// 判断是否有Token(一般用户登录之后,后端会返回给我们用户的Token, 我们将其存放下来,具体存 到哪里,存储多长时间,根据你的需要来,这里我是存储在 localStorage 里)if(localStorage.getItem('NOV_TOKEN')) {// 记住,一定要调用next函数,router才能进行下去next()} else {// 需要登录却没有登录(无Token),重定向到 登录页next({path: '/login',})}} else {// 不需要登录,直接往下进行next()}})
请求拦截
上面,我们对路由进行了拦截,但是有时候虽然我们登录过,将Token已经存储在 localStorage 里了,但这也不能完全保证安全,因为 token 一般是具有时效的,登录(Token)失效后也需要重新登录。但我们怎么知道Token是否失效了呐?这就需要后端告诉我们了。当我们进入一个需要登录的页面,需要发送请求获取某些数据时,后端会先判断此人是否有登录,登录是否失效,如果失效,后端会返回给我们401登录失效code码。当我们接收到 401 code 码时,就需要重定向到登录页,引导用户进行登录。
那这些,就属于请求拦截的范畴了。
axios的官方文档给出了两个拦截方式:
// 添加请求拦截器
axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
axios.interceptors.response.use(function (response) {// 对响应数据做点什么return response;}, function (error) {// 对响应错误做点什么return Promise.reject(error);});
所以我们就可以在这里进行响应拦截了
// 请求响应拦截处理
axios.interceptors.response.use(response => {const { data: { code, message } } = responseif ( code === '200') {return response;} else if (code === '401') {router.push('/login')} else {ElementUI.Message.error({message: message || '请求失败',type: 'warning'});}
}, function (error) {return Promise.reject(error)
})
关于这些axios请求,我们都是直接在main.js里进行相关处理的。当然,我们也可以对axios在进行一次封装,把请求相关的东西都放到里面去,这样代码更具有可读性和维护性。
首先,我们在src文件夹下面新建一个utils工具文件
在里面新建一个request.js文件,关于请求的信息我们就放到里面进行处理。
import axios from "axios";
import { Message, MessageBox } from "element-ui";
import store from "../store";
import { getToken, removeToken} from "./auth";
import { IDPHelper } from "./idp";// 创建axios实例
const service = axios.create({baseURL: process.env.BASE_API, // api的base_urltimeout: 15000,validateStatus: (status) => {return true;}
});// request拦截器
service.interceptors.request.use(config => {if (store.getters.token) {// config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改config.headers["token"] = getToken();}return config;},error => {// Do something with request errorconsole.log(error); // for debugPromise.reject(error);}
);// respone拦截器
service.interceptors.response.use(response => {const status = response.status;if (status >= 200 && status < 300) {return response.data;}switch (status) {case 401:MessageBox.confirm("你已被登出,请重新登录","确定登出",{confirmButtonText: "重新登录",cancelButtonText: "取消",type: "warning"}).then(() => {removeToken();router.push('/login')});break;case 403:MessageBox.confirm("你无权访问该页面,请申请权限再行访问","系统安全",{confirmButtonText: "回退",cancelButtonText: "继续留下",type: "warning"}).then(() => {window.history & window.history.back()});break;case 404:case 408:Message({message: "请求资源不存在!",type: "error",duration: 3 * 1000});break;case 500:case 502:case 504:default:Message({message: "服务器繁忙,请稍后重试!",type: "error",duration: 3 * 1000});break;}return Promise.reject("error");},error => {console.log("err" + error); // for debugMessage({message: error.message,type: "error",duration: 3 * 1000});return Promise.reject(error);}
);export default service;
使用: