Axios封装
文章目录
参考
前言
axios
常常被推荐使用进行网络请求,直接使用它其实也很方便,不过当项目扩展,网络接口变多时我们还需要对axios
进行二次封装
提示:以下是本篇文章正文内容,下面案例可供参考
准备工作
前端
创建一个react + typescript的脚手架
npx create-react-app ts-demo --template typescript
下载axios依赖
yarn add axios
后端
选择使用
gin
框架简单搭建,这里不详细说明
项目创建
go mod init demo
下载gin配置
go get github.com/gin-gonic/gin
代码
package main
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// 解决跨域问题的中间件
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
origin := c.Request.Header.Get("Origin")
if origin != "" {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-changeOrigin", "true")
c.Header("Access-Encoding", "*")
}
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
}
func DefaultGet(c *gin.Context) {
time.Sleep(time.Second * 2)
c.JSON(200, gin.H{
"Content": "Hello World!",
"code": 200,
})
}
func DefaultPost(c *gin.Context) {
c.String(200, "Hello, world!")
}
func server() {
router := gin.Default()
router.Use(Cors())
gr := router.Group("/api")
gr.GET("/text", DefaultGet)
gr.POST("/", DefaultPost)
router.Run(":8081")
}
func main(){
server()
}
axos 封装
配置信息
可以在进行编写后面之前先准备一些基础数据,比如状态码、动态的baseURL……
/**
* 状态码
*/
const ServerCode = {
SUCCESS: 200,
CONTINUE: 400,
WRONG_PARAM: 401,
WRONG_REQUEST: 402,
FORBIDDEN: 403,
WRONG_URL: 404,
NO_LOGIN: 406,
TIME_OUT: 408,
WRONG_REQUEST_MODAL: 410,
AUTO_SAVE_ERROR_CODE: 411, // 自动暂存code
PROCESS_ERROR: 412, // 任务进度错误
WRONG_SERVER: 500,
WRONG_REALIZE: 501,
WRONG_GATEWAY: 502,
BAD_SERVER: 503,
GATEWAY_TIME_OUT: 504,
WRONG_VERSION: 505
};
简单编写axios封装
// 进行统一配置
const service: AxiosInstance = axios.create({
baseURL: "http://localhost:8081/api/",
// 统一设置超时时间
timeout: 5000,
// 允许携带cookie
withCredentials: true,
})
值得注意的是
service(axiosConfig)
返回的是一个Promise对象
对request进行统一配置,并避免重复请求
// cancelToken存储
const source: { [key: string]: any } = {};
// 存储接口的url
let requestList: string[] = [];
/**
* 请求的统一配置
*/
service.interceptors.request.use(
(config:any) => {
if(/get/i.test(config.method)){
config.params = {
...config.params,
_t: Date.now()
}
config.headers = {
'Content-Type': '*',
...config.headers,
}
const requestIndex = requestList.findIndex(item => item === config.url);
// 阻止重复请求
if(requestIndex !== -1){
source[config.url] && source[config.url]("终止请求");
requestList.splice(requestIndex, 1);
}
// 存储阻止请求的method
config.cancelToken = new axios.CancelToken(cancel =>
source[config.url] = cancel
);
requestList.push(config.url);
}
return config;
},
err => {
// 设置请求出错时做的事
return Promise.reject(err);
}
)
统一response设置
/**
* response拦截器
*/
service.interceptors.response.use(
(response:any) => {
const {url, method} = response.config;
if(/get/i.test(method as Method)){
const requestIndex = requestList.findIndex(item => item === url);
// 收到相应后删去记录重复请求记录数组的信息
if(requestIndex > -1) {
requestList.splice(requestIndex, 1);
delete source[url];
}
}
const res = response.data;
if(res.code === ServerCode.SUCCESS) return res;
return Promise.reject({data:res, response});
},
(error:any) => {
// 统一错误拦截
return Promise.reject(error);
}
)
统一错误处理
/**
* 统一处理错误
* @param err 错误对象
* @returns
*/
const errorHandel: <R>(err:AxiosError<R>) => Promise<R> = err => {
if(axios.isCancel(err)) return Promise.reject(err);
let message = "";
if(err && err.response) {
switch(err.response.status) {
case 302: message = '接口重定向了!';break;
case 400: message = '参数不正确!';break;
case 401: message = '您未登录,或者登录已经超时,请先登录!';break;
case 403: message = '您没有权限操作!'; break;
case 404: message = `请求地址出错: ${err.response.config.url}`; break; // 在正确域名下
case 408: message = '请求超时!'; break;
case 409: message = '系统已存在相同数据!'; break;
case 500: message = '服务器内部错误!'; break;
case 501: message = '服务未实现!'; break;
case 502: message = '网关错误!'; break;
case 503: message = '服务不可用!'; break;
case 504: message = '服务暂时无法访问,请稍后再试!'; break;
case 505: message = 'HTTP版本不受支持!'; break;
default: message = '异常问题,请联系管理员!'; break
}
if(err?.message.includes('timeout')) message = '请求超时!';
if(err?.message.includes('Network Error')) message = '网络异常!';
}
console.log(message)
return Promise.reject(err);
}
统一处理请求返回的结果
/**
* 统一处理请求返回结果
* @param axiosObj
* @returns
*/
const myResponse: <R>(axiosObj: any) => Promise<R> = axiosObj => {
return axiosObj.then((res:any) => res).catch((err: any) => errorHandel(err));
}
这里需要结合Promise进行理解,要注意
myResponse
的返回值是一个Promise对象
对get、post、put、delete请求封装
/**
* 四种请求方式
* @param url 接口地址
* @param data 接口参数
* @param headers 接口所需header配置
* @param gateWay 用户验证信息
* @returns
*/
export const get: <R>(req:any) => Promise<R> = ({url, data, headers, gateWay }) =>
myResponse(
service.get(url, {
params: data,
headers,
// @ts-ignore
gateWay,
paramsSerializer: params => {
return qs.stringify(params, { indices: false });
}
})
)
export const post: <R>(req:any) => Promise<R> = ({url, data, headers, gateWay }) =>
// @ts-ignore
myResponse(service.post(url, data, {headers, gateWay}))
export const put: <R>(req:any) => Promise<R> = ({ url, data, headers, gateWay }) =>
// @ts-ignore
myResponse(service.put(url, data, { headers, gateWay }));
export const del: <R>(req:any) => Promise<R> = ({ url, data, headers, gateWay }) =>
// @ts-ignore
myResponse(service.delete(url, { data, headers, gateWay }));
使用
直接修改react项目的中
app.tsx
中的内容, 设置一个按钮然后触发请求
代码
import './App.css';
import axios from "axios";
import {get} from "./Axios/http" ;
function App() {
return (
<div className="App">
<header className="App-header">
<button
onClick={() => {
// 使用封装的get方法
get({url:"text"}).then((res: any) => console.log(res)).catch((err: any) => console.log(err))
// 直接使用axios
// axios({
// method: "get",
// timeout: 5000,
// url: "http://localhost:8081/api/text?_t=1656124014230",
// headers: {
// 'Content-Type': '*',
// },
// }).then(res => console.log(res)).catch((err: any) => console.log(err))
}}
>确定</button>
</header>
</div>
);
}
export default App;
可以看到封装后的使用起来比直接使用
axios
更加方便,而且其中内置了关于重复请求、错误处理等操作。
封装部分的完整代码
import qs from 'qs';
import axios, {AxiosInstance, Method, AxiosError} from "axios";
/**
* 状态码
*/
const ServerCode = {
SUCCESS: 200,
CONTINUE: 400,
WRONG_PARAM: 401,
WRONG_REQUEST: 402,
FORBIDDEN: 403,
WRONG_URL: 404,
NO_LOGIN: 406,
TIME_OUT: 408,
WRONG_REQUEST_MODAL: 410,
AUTO_SAVE_ERROR_CODE: 411, // 自动暂存code
PROCESS_ERROR: 412, // 任务进度错误
WRONG_SERVER: 500,
WRONG_REALIZE: 501,
WRONG_GATEWAY: 502,
BAD_SERVER: 503,
GATEWAY_TIME_OUT: 504,
WRONG_VERSION: 505
};
// 进行统一配置
const service: AxiosInstance = axios.create({
baseURL: "http://localhost:8081/api/",
timeout: 5000,
// 允许携带cookie
withCredentials: true,
})
// cancelToken存储
const source: { [key: string]: any } = {};
// 存储接口的url
let requestList: string[] = [];
/**
* 请求的统一配置
*/
service.interceptors.request.use(
(config:any) => {
if(/get/i.test(config.method)){
config.params = {
...config.params,
_t: Date.now()
}
config.headers = {
'Content-Type': '*',
...config.headers,
}
const requestIndex = requestList.findIndex(item => item === config.url);
// 阻止重复请求
if(requestIndex !== -1){
source[config.url] && source[config.url]("终止请求");
requestList.splice(requestIndex, 1);
}
config.cancelToken = new axios.CancelToken(cancel =>
source[config.url] = cancel
);
requestList.push(config.url);
}
return config;
},
err => {
// 设置请求出错时做的事
return Promise.reject(err);
}
)
/**
* response拦截器
*/
service.interceptors.response.use(
(response:any) => {
const {url, method} = response.config;
if(/get/i.test(method as Method)){
const requestIndex = requestList.findIndex(item => item === url);
if(requestIndex > -1) {
requestList.splice(requestIndex, 1);
delete source[url];
}
}
const res = response.data;
if(res.code === ServerCode.SUCCESS) return res;
return Promise.reject({data:res, response});
},
(error:any) => {
// 统一错误拦截
return Promise.reject(error);
}
)
/**
* 统一处理错误
* @param err 错误对象
* @returns
*/
const errorHandel: <R>(err:AxiosError<R>) => Promise<R> = err => {
if(axios.isCancel(err)) return Promise.reject(err);
let message = "";
if(err && err.response) {
switch(err.response.status) {
case 302: message = '接口重定向了!';break;
case 400: message = '参数不正确!';break;
case 401: message = '您未登录,或者登录已经超时,请先登录!';break;
case 403: message = '您没有权限操作!'; break;
case 404: message = `请求地址出错: ${err.response.config.url}`; break; // 在正确域名下
case 408: message = '请求超时!'; break;
case 409: message = '系统已存在相同数据!'; break;
case 500: message = '服务器内部错误!'; break;
case 501: message = '服务未实现!'; break;
case 502: message = '网关错误!'; break;
case 503: message = '服务不可用!'; break;
case 504: message = '服务暂时无法访问,请稍后再试!'; break;
case 505: message = 'HTTP版本不受支持!'; break;
default: message = '异常问题,请联系管理员!'; break
}
if(err?.message.includes('timeout')) message = '请求超时!';
if(err?.message.includes('Network Error')) message = '网络异常!';
}
console.log(message)
return Promise.reject(err);
}
/**
* 统一处理请求返回结果
* @param axiosObj
* @returns
*/
const myResponse: <R>(axiosObj: any) => Promise<R> = axiosObj => {
return axiosObj.then((res:any) => res).catch((err: any) => errorHandel(err));
}
/**
* 四种请求方式
* @param url 接口地址
* @param data 接口参数
* @param headers 接口所需header配置
* @param gateWay 用户验证信息
* @returns
*/
export const get: <R>(req:any) => Promise<R> = ({url, data, headers, gateWay }) =>
myResponse(
service.get(url, {
params: data,
headers,
// @ts-ignore
gateWay,
paramsSerializer: params => {
return qs.stringify(params, { indices: false });
}
})
)
export const post: <R>(req:any) => Promise<R> = ({url, data, headers, gateWay }) =>
// @ts-ignore
myResponse(service.post(url, data, {headers, gateWay}))
export const put: <R>(req:any) => Promise<R> = ({ url, data, headers, gateWay }) =>
// @ts-ignore
myResponse(service.put(url, data, { headers, gateWay }));
export const del: <R>(req:any) => Promise<R> = ({ url, data, headers, gateWay }) =>
// @ts-ignore
myResponse(service.delete(url, { data, headers, gateWay }));
export default service;