前言
最近为了熟悉Vue3+ts的开发,特意在项目练习时使用其进行开发,涉及到axios网络请求封装一块,确实从中学到了一些知识,特此总结一下
由于没有实习经历,文章中的封装方法仍有许多不足,希望各位读者能积极指出,互通有无,共同学习
http模块
一般封装axios都会在项目src目录新建一个http文件夹,然后创建一个request.ts进行axios封装
//我这里创建一个request类用来封装axios
class request{
private instance:AxiosInstance;
constructor(config:AxiosRequestConfig){
//初始化instance,创建一个axios实例
this.instance =axios.create(config)
//拦截器配置(axios中一个很重要的模块)
this.interceptors()
}
private interceptors(){
......
}
//拦截器配置函数和instance属性都是私有的,不向外暴漏
//下面封装各类网络请求方法
get(){....}
post(){......}
}
拦截器配置函数
private interceptors(){
//请求发送之前(一般的操作是要在此时配置token)(此时拦截器操作的是config)
this.instance.interceptors.request.use((config:AxiosRequestConfig)=>{
let token= getToken() || "" //getToken()为封装的工具函数(获取sessionStorage中的token,下面会说明)
if(token){ //存在token就加上
config.headers ={
token:token
}
}
return config
},(error:any)=>{
error.data ={};
error.data.msg ="请求发送异常!!"
return error
})
//-----------------------------------------------------------------------------------------------
//请求返回之后的处理(一般处理后端设置的特殊状态)
this.instance.interceptors.response.use((res:AxiosResponse)=>{
//此为响应成功,即http请求返回的状态码为200
if (res && res.data){
const data =res.data;
//此时查看后端返回的状态信息
if (data.code ==600){
//600仅为举例,具体情况看各自项目后端设置,比如此处600表示登录请求响应成功,但是无权限(token过期或者无token)
router.push({path:"/login"}) //跳转到登录
}else if (data.code ==200){
return res
}else{
ElMessage.error(data.msg || "服务器出错!!") //借助element-plus组件进行错误提示
return res || null;
}
}
},error=>{
//此处表示http响应失败,在各种项目的axios封装中都是通用的
error.data ={};
if (error && error.response){
switch(error.response.status){
case xxx:
err.data.msg ="xxxx";
ElMessage.error(error.data.msg)
break
.......
default:
error.data.msg =`连接错误${error.msg.status}`
ElMessage.error(error.data.msg)
}
}else {
error.data.msg = "连接到服务器失败";
ElMessage.error(error.data.msg)
}
return error;
})
}
http响应失败的状态码判断
case 400:
error.data.msg = '错误请求';
ElMessage.error(error.data.msg)
break
case 401:
error.data.msg = '未授权,请重新登录';
ElMessage.error(error.data.msg)
break
case 403:
error.data.msg = '拒绝访问';
ElMessage.error(error.data.msg)
break
case 404:
error.data.msg = '请求错误,未找到该资源';
ElMessage.error(error.data.msg)
break
case 405:
error.data.msg = '请求方法未允许';
ElMessage.error(error.data.msg)
break
case 408:
error.data.msg = '请求超时';
ElMessage.error(error.data.msg)
break
case 500:
error.data.msg = '服务器端出错';
ElMessage.error(error.data.msg)
break
case 501:
error.data.msg = '网络未实现';
ElMessage.error(error.data.msg)
break
case 502:
error.data.msg = '网络错误';
ElMessage.error(error.data.msg)
break
case 503:
error.data.msg = '服务不可用';
ElMessage.error(error.data.msg)
break
case 504:
error.data.msg = '网络超时';
ElMessage.error(error.data.msg)
break
case 505:
error.data.msg = 'http版本不支持该请求';
ElMessage.error(error.data.msg)
break
default:
error.data.msg = `连接错误${error.response.status}`;
ElMessage.error(error.data.msg)
get/post请求封装
这里其实有点争议,封装与不封装都可以,封装之后固然可以更加方便的使用,但是某些具体的请求可能和封装后有区别,这个时候就不能直接使用封装后的get/post函数,可以专门针对该函数重新封装(就变麻烦了)
import qs from "qs" //
//返回结果的接口(根据自己后端返回数据定义)
interface Result<T =any> {
code:number,
msg:string,
data :T
}
//Promise类型需要接收一个泛型参数(目的是为了定义响应数据的类型)
//T用于指定响应数据中data的类型
get<T =any>(url:string,params?:any):Promise<Result<T>>{
return new Promise((resolve,reject)=>{
//需要注意的是,当我们get请求需要携带query参数时,可以在paramsSerializer字段里指定处理函数
//将{username:"yzh",id:2} =>http://xxxx?username=yzh&id=2
this.instance.get<T>(url,{params:params,paramsSerializer:(params)=>{
return qs.stringify(params)}})
.then(res=>{resolve(res.data as any)})
.catch(err=>{reject(err)})
})
}
post<T =any>(url:string,params:any):Promise<Result<T>>{
return new Promise((resolve,reject)=>{
this.instance.post(url,parmas,{
//转换成json格式
transformRequest:[(params)=>{return JSON.stringify(params)}],
headers:{"Content-type":"application/json"}
}).then(res=>{resolve(res.data as any)})
.catch(err=>{reject(err)})
})
}
至此,http模块中的axios封装完成,我们将其导出,然后在http文件夹下新建http.ts文件,创建一个axios实例并导出,即可在其他需要调用axios的地方引入http.ts
http.ts
import request from "./request"
const http =new request({
//配置一下config
baseURL:"http://xxxxxxx",
timeout:10000
})
export default http
api模块
为了更好的管理各种请求,我们通常在src目录下新建api文件夹,然后在api文件夹下再分模块统一管理,比如user文件夹,menu文件夹等等(这样的好处还有,每个请求可能会定义各种请求参数接口以及返回结果接口,也可以统一管理在各模块中,降低在组件请求时业务逻辑的耦合度)
这里以user模块为例
userModel.ts
//定义各种接口
//登录请求参数接口
export interface LoginParam{
username:string,
password:string,
code:string
}
//登录返回结果接口
export interface LoginResult{
id:number,
token:string,
code:number,
expireTime:number
}
......
user.ts
import http from "@/http/http"
import { LoginParam, LoginResult } from "./userModel"
enum Api {
getImg ="/api/sysUser/image",
login ="/api/user/login",
getInfo ="/api/sysUser/getInfo"
}
// 获取验证码
export async function getImageApi(){
return await http.getImage(Api.getImg)
}
// 登录
export async function loginApi(params:LoginParam){
return await http.login<LoginResult>(Api.login,params)
}
工具类函数模块
所谓工具类函数,在项目中就不一定与axios有关,不过之前封装axios时,在请求拦截器中获取token却是可以封装到工具类模块中。
enum Keys{
Token = 'token',
UserId = 'userId',
ExpireTime = 'expireTime'
}
//存储token到session
export const setToken = (token:string)=>{
sessionStorage.setItem(Keys.Token,token)
}
export const getToken = ()=>{
return sessionStorage.getItem(Keys.Token)
}
...