likes
comments
collection
share

带你梳理我的Axios封装(TS)

作者站长头像
站长
· 阅读数 42

深入梳理Axios封装(TS版)

axios 封装思路

带你梳理我的Axios封装(TS)

实现

新建Http.tsx文件,创建一个Http的类,添加get、post、patch、delete四个选项。

import axios, {AxiosInstance, AxiosRequestConfig} from 'axios';

type GetConfig = Omit<AxiosRequestConfig, 'params' | 'url' | 'method'>
type PostConfig = Omit<AxiosRequestConfig, 'url' | 'data' | 'method'>
type PatchConfig = Omit<AxiosRequestConfig, 'url' | 'data'>
type DeleteConfig = Omit<AxiosRequestConfig, 'params'>

export class Http {
  instance: AxiosInstance;

  constructor(baseURL: string) {
    this.instance = axios.create({
      baseURL
    });
  }

  get<R = unknown>(url: string, query?: Record<string, JSONValue>, config?: GetConfig) {
    return this.instance.request<R>({...config, url: url, params: query, method: 'get'});
  }

  post<R = unknown>(url: string, data?: Record<string, JSONValue>, config?: PostConfig) {
    return this.instance.request<R>({...config, url, data, method: 'post'});
  }

  patch<R = unknown>(url: string, data?: Record<string, JSONValue>, config?: PatchConfig) {
    return this.instance.request<R>({...config, url, data, method: 'patch'});
  }

  delete<R =unknown>(url: string, query?: Record<string, string>, config?: DeleteConfig) {
    return this.instance.request<R>({...config, url: url, params: query, method: 'delete'});
  }
}

export const http = new Http('/api/v1');

Axios拦截器

设置请求太频繁429

http.instance.interceptors.response.use(
  (response) => {
    return response
  },
  (error) => {
    if (error.response) {
      const axiosError = error as AxiosError
      if (axiosError.response?.status === 429) {
        alert('你太频繁了')
      }
    }
    throw error
  }
)

保存得到的JWT

JWT定义

  • 原来的Session是给用户传随机数,需要和真实数据之间找对应关系,JWT是直接把数据加密传给用户,用户解密得到真实数据,就不需要存对应内容,拿到的内容就是内容本身,只是改不了,必须要解密才能得到
  • JWT由三部分组成:header头、payload身体、signature签名(密文)
  • header:头部有两部分组成,头需要标记用的加密算法是什么,字段叫alg,还可以写类型:typ, 可以是请求或其他类型
  • payload:把一个JSON写到Body里:UID等内容
  • signature:S等于加密方式:第一部分加密等于私钥,第二部分是base64的header,第三部分是base64的body S= 加密 (私钥, base64(header), base64(body)),加私钥是因为加密需要用到,第二部分的header是为了让加密算法不固定,header主要用来选不同的机密算法,第三部分的body才是前文所说的UID

JWT的用法

  • 先看看Session怎么做登录:前端发送post请求,得到Cookie,再get用户信息,服务器验证Cookie,由于浏览器会自动把Cookie加到请求里,所以这里前端不需要做操作就能得到相应的用户信息的JSON
  • JWT怎么操作,用户登录前端发送请求:post/JWT,然后服务器返回JWT内容,第一部分私密我们不关心,第二部分是UID比较关心,第三部分是密文,也不关心,前端不需要把UID读出来,因为前端能读那么黑客也能读。
      1. JWT不是存到Cookie里的,需要前端手动存到localStorage里面
      1. 配置axios,让库所有请求的header都带上JWT,一般带在Auth开头的字段
      1. 发请求 Get/user
    Toast加载中

    设置_autoLoading提示

    http.instance.interceptors.request.use((config) => {
      const jwt = localStorage.getItem('jwt')
      if (jwt) {
        config.headers!.Authorization = `Bearer ${jwt}`
      }
      if (config._autoLoading === true) {
        Toast.loading({
          message: '加载中...',
          forbidClick: true,
          duration: 0
        })
      }
      return config
    })
    

    使用示例

    登录拿到jwt

    const response = await http.post<{ jwt: string }>('/session', formData,   {_autoLoading: true})
              .catch(onError)
     localStorage.setItem('jwt', response.data.jwt)
    //forData onError 根据情况自定义
    

    拿到JWT使用

    await http.post<Resource<Item>>('/items', formData, {_autoLoading: true }).catch(onError)
    

    完整封装代码

    import axios, {AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse} from 'axios';
    import {Toast} from 'vant';
    
    type GetConfig = Omit<AxiosRequestConfig, 'params' | 'url' | 'method'>
    type PostConfig = Omit<AxiosRequestConfig, 'url' | 'data' | 'method'>
    type PatchConfig = Omit<AxiosRequestConfig, 'url' | 'data'>
    type DeleteConfig = Omit<AxiosRequestConfig, 'params'>
    
    export class Http {
      instance: AxiosInstance;
    
      constructor(baseURL: string) {
        this.instance = axios.create({
          baseURL
        });
      }
    
      get<R = unknown>(url: string, query?: Record<string, JSONValue>, config?: GetConfig) {
        return this.instance.request<R>({...config, url: url, params: query, method: 'get'});
      }
    
      post<R = unknown>(url: string, data?: Record<string, JSONValue>, config?: PostConfig) {
        return this.instance.request<R>({...config, url, data, method: 'post'});
      }
    
      patch<R = unknown>(url: string, data?: Record<string, JSONValue>, config?: PatchConfig) {
        return this.instance.request<R>({...config, url, data, method: 'patch'});
      }
    
      delete<R = unknown>(url: string, query?: Record<string, string>, config?: DeleteConfig) {
        return this.instance.request<R>({...config, url: url, params: query, method: 'delete'});
      }
    }
    
    export const http = new Http('api/v1');
    
    http.instance.interceptors.request.use((config) => {
      const jwt = localStorage.getItem('jwt');
      if (jwt) {
        config.headers!.Authorization = `Bearer ${jwt}`;
      }
      if (config._autoLoading === true) {
        Toast.loading({
          message: '加载中...',
          forbidClick: true,
          duration: 0
        });
      }
      return config;
    });
    
    http.instance.interceptors.response.use(
      (response) => {
        if (response.config._autoLoading === true) {
          Toast.clear();
        }
        return response;
      },
      (error: AxiosError) => {
        if (error.response?.config._autoLoading === true) {
          Toast.clear();
        }
        throw error;
      }
    );
    
    http.instance.interceptors.response.use(
      (response) => {
        return response;
      },
      (error) => {
        if (error.response) {
          const axiosError = error as AxiosError;
          if (axiosError.response?.status === 429) {
            alert('你太频繁了');
          }
        }
        throw error;
      }
    );
    
转载自:https://juejin.cn/post/7223558793845653541
评论
请登录