前端时间项目需要发布一个较大的版本,工作比较忙,加了好多个晚上的班,感觉自己有点缺氧了。最近稍微闲下来了,顺便调休了三天,刚刚给家里来了个大扫除,看着这干干净净的小家,心里顿时舒服了很多。
下面进入正题(本文以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 } } = response
if ( 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_url
timeout: 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 error
console.log(error); // for debug
Promise.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 debug
Message({
message: error.message,
type: "error",
duration: 3 * 1000
});
return Promise.reject(error);
}
);
export default service;
使用: