axios封装
需求及实现
- 统一捕获接口报错
- 弹窗提示
- 报错重定向
- 基础鉴权
- 表单序列化
实现的功能
- 统一捕获接口报错 : 用的axios内置的拦截器
- 弹窗提示: 引入
Element UI
的Message
组件 - 报错重定向: 路由钩子
- 基础鉴权: 服务端过期时间戳和token,还有借助路由的钩子
- 表单序列化: 我这边直接用
qs
(npm模块),你有时间也可以自己写
用法及封装
- 用法
// 服务层 , import默认会找该目录下index.js的文件,这个可能有小伙伴不知道
// 可以去了解npm的引入和es6引入的理论概念
import axiosPlugin from "./server";
Vue.use(axiosPlugin);
- 对axios的封装(AXIOS:
index.js
)
import axios from "axios";
import qs from "qs";
import { Message } from "element-ui";
import router from "../router";
const Axios = axios.create({
baseURL: "/", // 因为我本地做了反向代理
timeout: 10000,
responseType: "json",
withCredentials: true, // 是否允许带cookie这些
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8"
}
});
//POST传参序列化(添加请求拦截器)
Axios.interceptors.request.use(
config => {
// 在发送请求之前做某件事
if (
config.method === "post" ||
config.method === "put" ||
config.method === "delete"
) {
// 序列化
config.data = qs.stringify(config.data);
}
// 若是有做鉴权token , 就给头部带上token
if (localStorage.token) {
config.headers.Authorization = localStorage.token;
}
return config;
},
error => {
Message({
// 饿了么的消息弹窗组件,类似toast
showClose: true,
message: error,
type: "error.data.error.message"
});
return Promise.reject(error.data.error.message);
}
);
//返回状态判断(添加响应拦截器)
Axios.interceptors.response.use(
res => {
//对响应数据做些事
if (res.data && !res.data.success) {
Message({
// 饿了么的消息弹窗组件,类似toast
showClose: true,
message: res.data.error.message.message
? res.data.error.message.message
: res.data.error.message,
type: "error"
});
return Promise.reject(res.data.error.message);
}
return res;
},
error => {
// 用户登录的时候会拿到一个基础信息,比如用户名,token,过期时间戳
// 直接丢localStorage或者sessionStorage
if (!window.localStorage.getItem("loginUserBaseInfo")) {
// 若是接口访问的时候没有发现有鉴权的基础信息,直接返回登录页
router.push({
path: "/login"
});
} else {
// 若是有基础信息的情况下,判断时间戳和当前的时间,若是当前的时间大于服务器过期的时间
// 乖乖的返回去登录页重新登录
let lifeTime =
JSON.parse(window.localStorage.getItem("loginUserBaseInfo")).lifeTime *
1000;
let nowTime = new Date().getTime(); // 当前时间的时间戳
console.log(nowTime, lifeTime);
console.log(nowTime > lifeTime);
if (nowTime > lifeTime) {
Message({
showClose: true,
message: "登录状态信息过期,请重新登录",
type: "error"
});
router.push({
path: "/login"
});
} else {
// 下面是接口回调的satus ,因为我做了一些错误页面,所以都会指向对应的报错页面
if (error.response.status === 403) {
router.push({
path: "/error/403"
});
}
if (error.response.status === 500) {
router.push({
path: "/error/500"
});
}
if (error.response.status === 502) {
router.push({
path: "/error/502"
});
}
if (error.response.status === 404) {
router.push({
path: "/error/404"
});
}
}
}
// 返回 response 里的错误信息
let errorInfo = error.data.error ? error.data.error.message : error.data;
return Promise.reject(errorInfo);
}
);
// 对axios的实例重新封装成一个plugin ,方便 Vue.use(xxxx)
export default {
install: function(Vue, Option) {
Object.defineProperty(Vue.prototype, "$http", { value: Axios });
}
};
- 路由钩子的调整(Router:
index.js
)
import Vue from "vue";
import Router from "vue-router";
import layout from "@/components/layout/layout";
// 版块有点多,版块独立路由管理,里面都是懒加载引入
import customerManage from "./customerManage"; // 客户管理
import account from "./account"; //登录
import adManage from "./adManage"; // 广告管理
import dataStat from "./dataStat"; // 数据统计
import logger from "./logger"; // 日志
import manager from "./manager"; // 管理者
import putonManage from "./putonManage"; // 投放管理
import error from "./error"; // 服务端错误
import { Message } from "element-ui";
Vue.use(Router);
// 请跳过这一段,看下面的
const router = new Router({
hashbang: false,
mode: "history",
routes: [
{
path: "/",
redirect: "/adver",
component: layout,
children: [
...customerManage,
...adManage,
...dataStat,
...putonManage,
...manager,
...logger
]
},
...account,
...error
]
});
// 路由拦截
// 差点忘了说明,不是所有版块都需要鉴权的
// 所以需要鉴权,我都会在路由meta添加添加一个字段requireLogin,设置为true的时候
// 这货就必须走鉴权,像登录页这些不要,是可以直接访问的!!!
router.beforeEach((to, from, next) => {
if (to.matched.some(res => res.meta.requireLogin)) {
// 判断是否需要登录权限
if (window.localStorage.getItem("loginUserBaseInfo")) {
// 判断是否登录
let lifeTime =
JSON.parse(window.localStorage.getItem("loginUserBaseInfo")).lifeTime *
1000;
let nowTime = (new Date()).getTime(); // 当前时间的时间戳
if (nowTime < lifeTime) {
next();
} else {
Message({
showClose: true,
message: "登录状态信息过期,请重新登录",
type: "error"
});
next({
path: "/login"
});
}
} else {
// 没登录则跳转到登录界面
next({
path: "/login"
});
}
} else {
next();
}
});
export default router;
axios可配置的一些选项,其他的具体看官网说明哈
export default {
// 请求地址
url: "/user",
// 请求类型
method: "get",
// 请根路径
baseURL: "http://www.mt.com/api",
// 请求前的数据处理
transformRequest: [function(data) {}],
// 请求后的数据处理
transformResponse: [function(data) {}],
// 自定义的请求头
headers: { "x-Requested-With": "XMLHttpRequest" },
// URL查询对象
params: { id: 12 },
// 查询对象序列化函数
paramsSerializer: function(params) {},
// request body
data: { key: "aa" },
// 超时设置s
timeout: 1000,
// 跨域是否带Token
withCredentials: false,
// 自定义请求处理
adapter: function(resolve, reject, config) {},
// 身份验证信息
auth: { uname: "", pwd: "12" },
// 响应的数据格式 json / blob /document /arraybuffer / text / stream
responseType: "json",
// xsrf 设置
xsrfCookieName: "XSRF-TOKEN",
xsrfHeaderName: "X-XSRF-TOKEN",
// 下传和下载进度回调
onUploadProgress: function(progressEvent) {
Math.round(progressEvent.loaded * 100 / progressEvent.total);
},
onDownloadProgress: function(progressEvent) {},
// 最多转发数,用于node.js
maxRedirects: 5,
// 最大响应数据大小
maxContentLength: 2000,
// 自定义错误状态码范围
validateStatus: function(status) {
return status >= 200 && status < 300;
},
// 用于node.js
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// 用于设置跨域请求代理
proxy: {
host: "127.0.0.1",
port: 8080,
auth: {
username: "aa",
password: "2123"
}
},
// 用于取消请求
cancelToken: new CancelToken(function(cancel) {})
};
Vue+axios实现登陆拦截
一个项目学会vue全家桶+axios实现登录、拦截、登出功能,以及利用axios的http拦截器拦截请求和响应。
前言
该项目是利用了Github 提供的personal token作为登录token,通过token访问你的Repository List。通过这个项目学习如何实现一个前端项目中所需要的
登录及拦截、登出、token失效的拦截及对应 axios 拦截器的使用。
准备
你需要先生成自己的 Github Personal Token(生成Token)。
Token 生成后 访问 Demo,即可查看你的Repository List。
项目结构
.
├── README.md
├── dist // 打包构建后的文件夹
│ ├── build.js
│ └── build.js.map
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── assets
│ │ ├── css.css
│ │ ├── icon.css
│ │ └── logo.png
│ ├── constant
│ │ └── api.js // 配置api接口文件
│ ├── http.js // 封装fetch、post请求及http 拦截器配置文件
│ ├── index.vue
│ ├── login.vue
│ ├── main.js
│ ├── repository.vue
│ ├── router.js // 路由配置文件
│ └── store
│ ├── store.js
│ └── types.js // vuex types
└── webpack.config.js
技术栈
- Vue 2.0
- vue-router
- vuex
- axios
- vue-material
登录拦截逻辑
第一步:路由拦截
首先在定义路由的时候就需要多添加一个自定义字段requireAuth
,用于判断该路由的访问是否需要登录。如果用户已经登录,则顺利进入路由,
否则就进入登录页面。
const routes = [
{
path: '/',
name: '/',
component: Index
},
{
path: '/repository',
name: 'repository',
meta: {
requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
},
component: Repository
},
{
path: '/login',
name: 'login',
component: Login
}
];
定义完路由后,我们主要是利用vue-router
提供的钩子函数beforeEach()
对路由进行判断。
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
if (store.state.token) { // 通过vuex state获取当前的token是否存在
next();
}
else {
next({
path: '/login',
query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由
})
}
}
else {
next();
}
})
每个钩子方法接收三个参数:
* to: Route: 即将要进入的目标 路由对象
* from: Route: 当前导航正要离开的路由
* next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
* next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
* next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
* next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
确保要调用 next 方法,否则钩子就不会被 resolved。
完整的方法见
/src/router.js
其中,to.meta
中是我们自定义的数据,其中就包括我们刚刚定义的requireAuth
字段。通过这个字段来判断该路由是否需要登录权限。需要的话,同时当前应用不存在token,则跳转到登录页面,进行登录。登录成功后跳转到目标路由。
登录拦截到这里就结束了吗?并没有。这种方式只是简单的前端路由控制,并不能真正阻止用户访问需要登录权限的路由。还有一种情况便是:当前token失效了,但是token依然保存在本地。这时候你去访问需要登录权限的路由时,实际上应该让用户重新登录。
这时候就需要结合 http 拦截器 + 后端接口返回的http 状态码来判断。
第二步:拦截器
要想统一处理所有http请求和响应,就得用上 axios 的拦截器。通过配置http response inteceptor
,当后端接口返回401 Unauthorized(未授权)
,让用户重新登录。
// http request 拦截器
axios.interceptors.request.use(
config => {
if (store.state.token) { // 判断是否存在token,如果存在的话,则每个http header都加上token
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
},
err => {
return Promise.reject(err);
});
// http response 拦截器
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 返回 401 清除token信息并跳转到登录页面
store.commit(types.LOGOUT);
router.replace({
path: 'login',
query: {redirect: router.currentRoute.fullPath}
})
}
}
return Promise.reject(error.response.data) // 返回接口返回的错误信息
});
完整的方法见
/src/http.js
.
通过上面这两步,就可以在前端实现登录拦截了。登出
功能也就很简单,只需要把当前token清除,再跳转到首页即可。
关于axios
对于axios,很多刚开始学习vue的人都觉得文档比较难以看懂。我刚开始也是这么觉得的。但通过这么一个项目下来后,发现axios并不难理解。建议在学习axios的时带着下面的目的去看文档会更高效。因为掌握了下面这些内容,基本上就可以无障碍得在项目中使用axios了。
* 发起http请求的方法
* http 请求成功时返回的数据及其类型
* http请求失败的处理
* 拦截器的使用
* http的配置
axios文档
运行及构建
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
需求及实现
一个项目学会vue全家桶+axios实现登录、拦截、登出功能,以及利用axios的http拦截器拦截请求和响应。