vue3+ts 封装接口请求

1、在src目录下新建service文件夹(名字看自己喜好,一般是service),里面存放接口请求的封装文件和公共配置文件。

(1)新建config.ts文件,里面统一管理请求的基础路径、请求超时时间和请求头等,如下:

/**
 * 公共配置
 */
const requestPublicConfig = {
  baseUrl: import.meta.env.MODE === "development" ? "/devApi" : "/mockApi",
  timeout: 10000,
  headers: {
    Accept: 'application/json, text/plain, */*',
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest'
  },
};

export default requestPublicConfig;

(2)新建request.ts文件,里面包含对发送请求前的处理、响应结果后的处理等,如下:

  • 导入相关依赖、添加默认配置
/**
 * 请求封装
 * 1.下载依赖
 * 2.创建文件
 * 3.导入/引入 axios
 * 4.添加默认配置
 * 5.定义返回的数据类型
 * 6.添加拦截器
 * 7.封装请求方法
 * 8.导出/抛出 实例
 */
import axios, { AxiosInstance, AxiosResponse, AxiosError, InternalAxiosRequestConfig, AxiosRequestConfig, Method } from "axios";
import { useRouter } from "vue-router";
import requestPublicConfig from "./config";
import { ElMessage } from "element-plus";

//自定义请求配置
export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
  isLoading?: boolean
  encrypt?: boolean
}
//默认配置
const defaultConfig = {
  baseURL: requestPublicConfig.baseUrl,//接口地址  TODO: 根据不同的环境需要更换
  timeout: requestPublicConfig.timeout,//超时时间
  headers: requestPublicConfig.headers,//请求头设置
  withCredentials: true,// 跨域时候允许携带凭证
  isLoading: true,//是否显示loading
  encrypt: true,//加解密开关
}
  • 定义取消重复请求
// 定义接口
interface PendingType {
  url?: string;
  method?: Method;
  params: any;
  data: any;
  cancel: any;
}

// 取消重复请求
const pending: Array<PendingType> = [];
const CancelToken = axios.CancelToken;
// 移除重复请求
const cancelDuplicateRequests = (config: AxiosRequestConfig) => {
  for (const key in pending) {
    const item: number = +key;
    const list: PendingType = pending[key];
    // 当前请求在数组中存在时执行取消操作
    if (list.url === config.url && list.method === config.method && JSON.stringify(list.params) === JSON.stringify(config.params) && JSON.stringify(list.data) === JSON.stringify(config.data)) {
      // 执行取消操作
      list.cancel('操作太频繁,请稍后再试');
      // 从数组中移除记录
      pending.splice(item, 1);
    }
  }
};
  • 定义对请求失败状态码的统一处理
//请求失败状态码统一处理
const checkStatus = (status: number | string) => {
  switch (Number(status)) {
    case 302:
      ElMessage.error('接口重定向了!');
      break;
    case 400:
      ElMessage.error("请求错误" + status)
      break;
    case 401:
      ElMessage.error("当前用户未授权,请进行身份验证" + status)
      localStorage.removeItem("userInfo");
      localStorage.removeItem("token");
      router.replace({
        path: '/login',
      });
      break;
    case 403:
      ElMessage.error("登录过期,token失效" + status)
      localStorage.removeItem("userInfo");
      setTimeout(() => {
        router.replace({
          path: '/login',
        });
      }, 1000);
      break;
    case 404:
      ElMessage.error("请求的资源不存在" + status)
      break;
    case 500:
      ElMessage.error("服务器发生错误" + status)
      break;
    case 502:
      ElMessage.error("网关错误" + status)
      break;
    case 503:
      ElMessage.error("服务不可用,服务器暂时处于维护中" + status)
      break;
    default:
      ElMessage.error("发生未知错误" + status)
  }
}
  • 创建axios实例
//实例化请求配置
const service: AxiosInstance = axios.create(defaultConfig);
  • 添加请求拦截器,对发送请求之前进行一些处理,例如取消重复的请求、添加token到请求头中,添加loading加载等
//请求拦截器
service.interceptors.request.use((config) => {
  //发送请求前,做些事情 例如取消重复请求、添加token到请求头中、添加loading的加载等
  cancelDuplicateRequests(config);// 取消重复请求
  config.cancelToken = new CancelToken((msg) => {
    pending.push({
      url: config.url,
      method: config.method as Method,
      params: config.params,
      data: config.data,
      cancel: msg,
    });
    //去localStorage中获取token
    let token = localStorage.getItem("token");
    if (token) {
      //添加token到请求头中
      config.headers['Authorization'] = "Bearer" + token;
    }
  });
  return config;
}, (error) => {
  return Promise.reject(error);
});
  • 添加响应拦截器,对响应数据进行处理 例如 取消loading的加载、对返回状态进行判断:如请求错误、请求超时、获取数据失败、暂无数据等
//响应拦截器 
service.interceptors.response.use((response: AxiosResponse) => {
  //对响应数据进行处理 例如 1、取消loading的加载;2、对返回状态进行判断:如请求错误、请求超时、获取数据失败、暂无数据等
  const { status, data, config } = response;
  cancelDuplicateRequests(config);
  // 请求成功
  if (status === 200 || status === 204) {
    return Promise.resolve(data);
  } else {
    return Promise.reject(config);
  }
}, (error: AxiosError) => {
  // 请求失败
  const { response } = error;
  if (response) {
    checkStatus(response?.status);
  }
});
  • 封装请求方法并导出
//封装请求方法
const httpRequest = {

  //get请求 获取数据 例如:获取列表、获取数据详情、搜索内容
  async get<T>(url: string, params?: any): Promise<T> {
    return await service.get(url, { params });

  },
  //post请求 添加数据 例如:表单提交、文件上传、添加新内容
  async post<T>(url: string, data?: any): Promise<T> {
    return await service.post(url, data);
  },
  //put请求 修改数据 例如:更新内容
  async put<T>(url: string, data?: any): Promise<T> {
    return await service.put(url, data);
  },
  //delete请求 删除数据 例如:删除内容
  async delete<T>(url: string, params?: any): Promise<T> {
    return await service.delete(url, { params });
  }
}


export default httpRequest;

2、在vite.config.ts文件中配置代理,如下:

 // 配置本地服务
    server: {
      host: true, //0.0.0.0或者true为监听所有ip
      port: 5000, //端口号
      open: false, //是否自动打开浏览器
      // hmr: true,//热更新
      //反向代理
      proxy: {
        "/devApi": {
          target: "http://127.0.0.1:4523/m1/5561793-5238912-default", //代理地址 目标后端服务地址
          changeOrigin: true, //开启跨域
          rewrite: (path) => path.replace(/^\/devApi/, ""), //重写路径,将/devApi替换为""
        },
        "/mockApi": {
          target: "http://127.0.0.1:5000", //代理地址 mock环境后端服务地址
          changeOrigin: true, //开启跨域
          rewrite: (path) => path.replace(/^\/mockApi/, ""), //重写路径,将/mockApi替换为""
        },
      },
    },

3、在src目录下新建api文件夹,里面存放各种业务接口,例如获取用户信息接口,如下:

import httpRequest from "@/service/request";

// 获取用户信息
export const getUserInfo = (params: Object) => {
	return httpRequest.get("/userApi/userInfo",params)
}

4、在业务组件和用户页面中调用存放在api文件夹中接口,如下:

import {getUserInfo} from "@/api/user";
onMounted(async () => {
  try {
    let res = await getUserInfo({token: "12345aBc67890dEf"});
    console.log("接口返回的用户信息", res);
  } catch (e) {
    console.log(e);
  }
});

启动项目,打开浏览器预览,效果如下:

至此,通过封装的方法调用接口可以正常获取返回来的数据。

多说一句:当我们改变响应状态码时,例如改成404,会发现出现了下述问题:

这是因为在对404响应状态码做了用element-plus的message弹出异常信息处理,如下:

而项目中采用的是自动按需导入 element-plus 组件,没有引入 element-plus 的全局样式导致样式缺失。

解决方法:在main.ts文件中添加如下代码:

import 'element-plus/dist/index.css'; // 引入Element Plus的样式

效果如下:

【PS:若有其它更完善的地方,请您大声说出来,欢迎大家一起探讨】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值