基于axios的HttpClient请求封装

项目使用代码 个人封装 可直接复制粘贴使用。
项目是基于react的 需结合自己项目使用框架进行适当修改。

import { message as Message } from 'antd'
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'

export const PRD_URL_PREFIX_ACTIVITY = '/api/activity/'
export const PRD_URL_PREFIX_BASE = '/api'

export type IErrorHandler = (statusCode: number, message: string) => void
const defaultErrorHandler: IErrorHandler = (statusCode, message) => {
  Message.error(typeof message === 'string' ? message : JSON.stringify(message))
}

export interface IRequestConfig extends AxiosRequestConfig {
  // 是否阻止提示错误消息
  suppressErrorMessage?: boolean
}
export interface IResponse<T = any> {
  status: string
  data: T
  message?: string
}
export interface IResponse2<T = any> {
  code: string | number
  data: T
  message: string
}

export class HttpClient {
  // 是否为生产环境
  private isProduction: boolean = false

  constructor(isProduction: boolean, errorHandler?: IErrorHandler) {
    this.isProduction = Boolean(isProduction)
    if (errorHandler) {
      this.errorHandler = errorHandler
    }
  }

  // 错误处理函数
  public errorHandler: IErrorHandler = (statusCode: number, message: string) => {
    console.error(message)
  }

  /**
   * 为发布环境处理URL
   */
  public dealUrlForPrd(url: string) {
    return this.isProduction ? url.replace(/^\/?api/g, '/marketApi') : url
  }

  /**
   * 转义查询参数
   */
  public encode(queryString?: string | null): string {
    if (!queryString) {
      return ''
    }

    return encodeURIComponent(queryString.replace(/[-[\]{}()*+?.,\\/^$|#]/g, '\\$&'))
  }

  /**
   * get查询
   */
  public async get<T = any>(url: string, requestConfig: IRequestConfig = {}) {
    const { suppressErrorMessage, ...config } = requestConfig

    try {
      const response: AxiosResponse<T> = await axios({
        url: this.getUrlWithTimestamp(this.dealUrlForPrd(url)),
        method: 'GET',
        headers: {
          'content-type': 'application/json; charse=UTF-8',
        },
        ...config,
      })
      return this.handleSuccess<T>(response, suppressErrorMessage)
    } catch (e) {
      this.handleError(e, suppressErrorMessage)
      throw e
    }
  }

  /**
   * post查询
   */
  public async post<T = any>(
    url: string,
    obj?: object | string,
    requestConfig: IRequestConfig = {}
  ): Promise<T> {
    const { suppressErrorMessage, ...config } = requestConfig

    let data = {}
    if (typeof obj === 'string') {
      try {
        data = JSON.parse(obj)
      } catch (e) {
        throw new Error(`请求参数解析失败\n${obj}\n${url}\n${e}`)
      }
    } else if (typeof obj === 'object') {
      data = obj
    }

    try {
      const response: AxiosResponse<T> = await axios({
        url: this.dealUrlForPrd(url),
        method: 'POST',
        headers: {
          'content-type': 'application/json; charse=UTF-8',
        },
        data,
        ...config,
      })
      return this.handleSuccess<T>(response, suppressErrorMessage)
    } catch (e) {
      this.handleError(e, suppressErrorMessage)
      throw e
    }
  }

  /**
   * del请求
   */
  public async del<T = any>(url: string, requestConfig: IRequestConfig = {}): Promise<T> {
    const { suppressErrorMessage, ...config } = requestConfig

    try {
      const response: AxiosResponse<T> = await axios({
        url: this.getUrlWithTimestamp(this.dealUrlForPrd(url)),
        method: 'DELETE',
        headers: {
          'content-type': 'application/json; charse=UTF-8',
        },
        ...config,
      })
      return this.handleSuccess<T>(response, suppressErrorMessage)
    } catch (e) {
      this.handleError(e, suppressErrorMessage)
      throw e
    }
  }

  /**
   * 上传文件
   */
  public async uploadFile<T = any>(
    url: string,
    file: File,
    requestConfig: IRequestConfig = {}
  ): Promise<T> {
    const { suppressErrorMessage, ...config } = requestConfig
    const formData = new FormData()

    try {
      formData.append('file', file)
      const response: AxiosResponse<T> = await axios.post(url, formData, {
        headers: {
          'content-type': 'multipart/form-data',
        },
        ...config,
      })
      return this.handleSuccess<T>(response, suppressErrorMessage)
    } catch (e) {
      this.handleError(e, suppressErrorMessage)
      throw e
    }
  }

  /**
   * 处理请求成功
   */
  private handleSuccess<T>(response: AxiosResponse, suppressErrorMessage: boolean = false): T {
    let message: string | undefined
    switch (response.status) {
      case 200:
        break

      case 400:
        message = '请求参数错误!'
        break

      case 401:
        message = '认证失败!'
        break

      case 403:
        message = '没有访问权限!'
        break

      case 415:
        message = '请求方式错误!'
        break

      default:
        message = '未知状态错误!'
        break
    }

    if (message && !suppressErrorMessage) {
      this.errorHandler(response.status, message)
    }

    // tslint:disable-next-line: no-object-literal-type-assertion
    return response.data || ({} as T)
  }

  /**
   * 处理请求失败
   */
  private handleError(error: AxiosError, suppressErrorMessage: boolean = false): void {
    let status: number = 0
    let message: string = '请求出错'

    /* 构造错误消息 */
    if (error.response) {
      status = error.response.status
      if (error.response.data.message) {
        message = error.response.data.message
      } else if (error.response.data.error) {
        message = error.response.data.error
      } else if (error.response.data.detail) {
        message = error.response.data.detail
      } else {
        if (status === 404) {
          message = error.message
        } else {
          message = error.response.data
        }
      }
    } else if (error.request) {
      status = error.request.status
      message = error.request.responseText
    } else {
      message = error.message
    }

    if (!suppressErrorMessage) {
      this.errorHandler(status, message)
    }
  }

  /**
   * 构造请求地址,添加时间戳参数,在开发环境禁用缓存
   * @param url 请求地址
   */
  private getUrlWithTimestamp(url: string): string {
    return this.isProduction
      ? url
      : `${url}${url.indexOf('?') > -1 ? '&' : '?'}_=${new Date().getTime()}`
  }
}

export default new HttpClient(process.env.REACT_APP_ENV!.trim() === 'prd', defaultErrorHandler)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 当下数九寒天,寒风凛冽,一向怕冷的我今天丝毫感觉不到有一丝丝的凉意,因为我的心被一名六年级名叫程笑千的女生给...
    江左镇刘楼小学程盼婷阅读 641评论 0 2
  • 以前走过的路 有遗憾 但不后悔 现在要走的路 希望不留遗憾 不后悔 喵喵君
    喵喵诗茵阅读 112评论 0 2
  • 记录反思 2:30到酒店,3:00才睡,中间醒来无数次,高原反应慢慢显现,晚上睡觉头疼,以为空气不好,打开空调、打...
    冬梅姐自我管理阅读 111评论 0 0
  • 不知不觉,在忙碌的闲暇忽然想到闹闹快两岁半了。 那个身长一尺多点的婴孩儿,现在居然快一米了。他躺在...
    韦心草阅读 499评论 1 1