当前位置:   article > 正文

前端框架搭建(五)封装请求接口【vite】_vite request

vite request

1.创建service目录——存放接口相关文件

创建目录结构如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CdDksDHi-1672049178995)(assets/image-20221221154128-mv1fptm.png)]

  • controller:前端直接与后端通信的层
  • dto:后端传给前端的
  • vo:前端传给后端的
  • request:封装请求接口的

2.安装axios依赖

使用 NPM:

npm install axios

  • 1
  • 2

使用 Yarn:

yarn axios
  • 1

使用 PNPM:

pnpm i aixos
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7MVT6LuC-1672049178995)(assets/image-20221221155333-4a0pkxo.png)]

安装成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTpk18WA-1672049178996)(assets/image-20221221155417-1jw34u7.png)]

3.封装axios请求

service目录下创建instance.ts文件,创建一个axios请求类

export default class AxiosInstance {
  

}
  • 1
  • 2
  • 3
  • 4

创建类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3xMws0gq-1672049178996)(assets/image-20221221161438-kpxnzic.png)]

/** 请求的相关类型 */
declare namespace Service {
    /**
     * 请求的错误类型:
     * - axios: axios错误:网络错误, 请求超时, 默认的兜底错误
     * - http: 请求成功,响应的http状态码非200的错误
     * - backend: 请求成功,响应的http状态码为200,由后端定义的业务错误
     */
    type RequestErrorType = 'axios' | 'http' | 'backend';
  
    /** 请求错误 */
    interface RequestError {
      /** 请求服务的错误类型 */
      type: RequestErrorType;
      /** 错误码 */
      code: string | number;
      /** 错误信息 */
      msg: string;
    }
  
    /** 后端接口返回的数据结构配置 */
    interface BackendResultConfig {
      /** 表示后端请求状态码的属性字段 */
      codeKey: string;
      /** 表示后端请求数据的属性字段 */
      dataKey: string;
      /** 表示后端消息的属性字段 */
      msgKey: string;
      /** 后端业务上定义的成功请求的状态 */
      successCode: number | string;
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

定义属性

    instance: AxiosInstance;

    backendConfig: Service.BackendResultConfig;
  • 1
  • 2
  • 3

定义构造方法

/**
   *
   * @param axiosConfig - axios配置
   * @param backendConfig - 后端返回的数据配置
   */
  constructor(
    axiosConfig: AxiosRequestConfig,
    backendConfig: Service.BackendResultConfig = {
      codeKey: 'code',
      dataKey: 'data',
      msgKey: 'message',
      successCode: 200
    }
  ) {
    this.backendConfig = backendConfig;
    this.instance = axios.create(axiosConfig);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

4.设置请求拦截器

搭建整体架构

/** 设置请求拦截器 */
    setInterceptor(){
      // 拦截请求
      this.instance.interceptors.request.use(
  
      )
      // 拦截响应
      this.instance.interceptors.response.use(

      );
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

①拦截请求

处理正常请求

在拦截请求处可添加对token或者数据的转换

async (config:AxiosRequestConfig<any>) => {
      
          const handleConfig = {...config};
          if (handleConfig.headers) {
            // 数据转换

            // 可在此处添加设置token
          }
       },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

添加基本配置config文件

放在utils文件目录下

/** 请求超时时间 */
export const REQUEST_TIMEOUT = 60 * 1000;

/** 错误信息的显示时间 */
export const ERROR_MSG_DURATION = 3 * 1000;

/** 默认的请求错误code */
export const DEFAULT_REQUEST_ERROR_CODE = 'DEFAULT';

/** 默认的请求错误文本 */
export const DEFAULT_REQUEST_ERROR_MSG = '请求错误~';

/** 请求超时的错误code(为固定值:ECONNABORTED) */
export const REQUEST_TIMEOUT_CODE = 'ECONNABORTED';

/** 请求超时的错误文本 */
export const REQUEST_TIMEOUT_MSG = '请求超时~';

/** 网络不可用的code */
export const NETWORK_ERROR_CODE = 'NETWORK_ERROR';

/** 网络不可用的错误文本 */
export const NETWORK_ERROR_MSG = '网络不可用~';

/** 请求不成功各种状态的错误 */
export const ERROR_STATUS = {
  400: '400: 请求出现语法错误~',
  401: '401: 用户未授权~',
  403: '403: 服务器拒绝访问~',
  404: '404: 请求的资源不存在~',
  405: '405: 请求方法未允许~',
  408: '408: 网络请求超时~',
  500: '500: 服务器内部错误~',
  501: '501: 服务器未实现请求功能~',
  502: '502: 错误网关~',
  503: '503: 服务不可用~',
  504: '504: 网关超时~',
  505: '505: http版本不支持该请求~',
  [DEFAULT_REQUEST_ERROR_CODE]: DEFAULT_REQUEST_ERROR_MSG
};

/** 不弹出错误信息的code */
export const NO_ERROR_MSG_CODE: (string | number)[] = [];

/** token失效需要刷新token的code(这里的66666只是个例子,需要将后端表示token过期的code填进来) */
export const REFRESH_TOKEN_CODE: (string | number)[] = [66666];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

统一处理请求异常

(axiosError: AxiosError) => {
          console.log('axiosError',axiosError)
      }
  • 1
  • 2
  • 3

②拦截响应

// 拦截响应
      this.instance.interceptors.response.use(
        (response:AxiosResponse<any, any>)=>{
          // 网络返回码
          const { status } = response;
          if (status === 200 || status < 300 || status === 304) {
            const backend = response.data;
            const { codeKey, dataKey, successCode } = this.backendConfig;
            // 请求成功
            if (backend[codeKey] === successCode) {
              return backend[dataKey];
            }

          }

        }
      );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

5.添加创建请求接口

添加请求接口类型

type RequestMethod = 'get' | 'post' | 'put' | 'delete';
  • 1

添加请求参数类型

interface RequestParam {
    url: string;
    method?: RequestMethod;
    data?: any;
    axiosConfig?: AxiosRequestConfig;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

创建请求

/**
 * 创建请求
 * @param axiosConfig - axios配置
 * @param backendConfig - 后端接口字段配置
 */
export function createRequest(axiosConfig: AxiosRequestConfig, backendConfig?: Service.BackendResultConfig) {
    const customInstance = new CustomAxiosInstance(axiosConfig, backendConfig);

  /**
   * 异步promise请求
   * @param param - 请求参数
   * - url: 请求地址
   * - method: 请求方法(默认get)
   * - data: 请求的body的data
   * - axiosConfig: axios配置
   */
  async function asyncRequest<T>(param: RequestParam): Promise<Service.RequestResult<T>> {
    const { url } = param;
    const method = param.method || 'get';
    const { instance } = customInstance;
    const res = await getRequestResponse({
      instance,
      method,
      url,
      data: param.data,
      config: param.axiosConfig
    });

    return res;
  }

}

async function getRequestResponse(params: {
    instance: AxiosInstance;
    method: RequestMethod;
    url: string;
    data?: any;
    config?: AxiosRequestConfig;
  }) {
    const { instance, method, url, data, config } = params;
  
    let res: any;
    if (method === 'get' || method === 'delete') {
      res = await instance[method](url, config);
    } else {
      res = await instance[method](url, data, config);
    }
    return res;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

添加请求方法(get、post等)


  /**
   * get请求
   * @param url - 请求地址
   * @param config - axios配置
   */
  function get<T>(url: string, config?: AxiosRequestConfig) {
    return asyncRequest<T>({ url, method: 'get', axiosConfig: config });
  }

  /**
   * post请求
   * @param url - 请求地址
   * @param data - 请求的body的data
   * @param config - axios配置
   */
  function post<T>(url: string, data?: any, config?: AxiosRequestConfig) {
    return asyncRequest<T>({ url, method: 'post', data, axiosConfig: config });
  }
  /**
   * put请求
   * @param url - 请求地址
   * @param data - 请求的body的data
   * @param config - axios配置
   */
  function put<T>(url: string, data?: any, config?: AxiosRequestConfig) {
    return asyncRequest<T>({ url, method: 'put', data, axiosConfig: config });
  }

  /**
   * delete请求
   * @param url - 请求地址
   * @param config - axios配置
   */
  function handleDelete<T>(url: string, config: AxiosRequestConfig) {
    return asyncRequest<T>({ url, method: 'delete', axiosConfig: config });
  }

  return {
    get,
    post,
    put,
    delete: handleDelete
  };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

6.创建请求接口

创建环境配置

在根目录下创建.env-config.ts对请求服务进行配置

/** 请求服务的环境配置 */
type ServiceEnv = Record<ServiceEnvType, ServiceEnvConfig>;

/** 不同请求服务的环境配置 */
const serviceEnv: ServiceEnv = {
  dev: {
    url: 'http://localhost:8080',
    urlPattern: '/url-pattern',
    secondUrl: 'http://localhost:8081',
    secondUrlPattern: '/second-url-pattern'
  },
  test: {
    url: 'http://localhost:8080',
    urlPattern: '/url-pattern',
    secondUrl: 'http://localhost:8081',
    secondUrlPattern: '/second-url-pattern'
  },
  prod: {
    url: 'http://localhost:8080',
    urlPattern: '/url-pattern',
    secondUrl: 'http://localhost:8081',
    secondUrlPattern: '/second-url-pattern'
  }
};

/**
 * 获取当前环境模式下的请求服务的配置
 * @param env 环境
 */
export function getServiceEnvConfig(env: ImportMetaEnv) {
  const { VITE_SERVICE_ENV = 'dev' } = env;

  const config = serviceEnv[VITE_SERVICE_ENV];

  return config;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

设置对应的类型

在typing中创建.env.d.ts

/**
 *后台服务的环境类型
 * - dev: 后台开发环境
 * - test: 后台测试环境
 * - prod: 后台生产环境
 */
 type ServiceEnvType = 'dev' | 'test' | 'prod';

 /** 后台服务的环境配置 */
 interface ServiceEnvConfig {
   /** 请求地址 */
   url: string;
   /** 匹配路径的正则字符串, 用于拦截地址转发代理(任意以 /开头 + 字符串, 单个/不起作用) */
   urlPattern: '/url-pattern';
   /** 另一个后端请求地址(有多个不同的后端服务时) */
   secondUrl: string;
   /** 匹配路径的正则字符串, 用于拦截地址转发代理(任意以 /开头 + 字符串, 单个/不起作用) */
   secondUrlPattern: '/second-url-pattern';
 }
 
 interface ImportMetaEnv {
   /** 项目基本地址 */
   readonly VITE_BASE_URL: string;
   /** 项目名称 */
   readonly VITE_APP_NAME: string;
   /** 项目标题 */
   readonly VITE_APP_TITLE: string;
   /** 项目描述 */
   readonly VITE_APP_DESC: string;
   /** iconify图标作为组件的前缀 */
   readonly VITE_ICON_PREFFIX: string;
   /**
    * 本地SVG图标作为组件的前缀, 请注意一定要包含 VITE_ICON_PREFFIX
    * - 格式 {VITE_ICON_PREFFIX}-{本地图标集合名称}
    * - 例如:icon-local
    */
   readonly VITE_ICON_LOCAL_PREFFIX: string;
   /** 后端服务的环境类型 */
   readonly VITE_SERVICE_ENV?: ServiceEnvType;
   /** hash路由模式 */
   readonly VITE_HASH_ROUTE?: 'Y' | 'N';
   /** 是否是部署的vercel */
   readonly VITE_VERCEL?: 'Y' | 'N';
 }
 
 interface ImportMeta {
   readonly env: ImportMetaEnv;
 }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

创建接口

import { getServiceEnvConfig } from '~/.env-config';
import { createRequest } from "./request";

const { url } = getServiceEnvConfig(import.meta.env);


export const web = createRequest({baseURL: url})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

7.测试登录接口

创建user子目录

import { web } from "../../request";

export function login(){
    return web.post<String>('/user/login')
}
  • 1
  • 2
  • 3
  • 4
  • 5

统一导出

export * as userRequest from './user/request'

  • 1
  • 2

使用

<script setup lang="ts">


const reuqest = () => userRequest.login()
</script>

<template>
  <h1 class="text-25px text-#ff6700 bg-#ccc">{{ msg }}</h1>

  <button type="button" @click="reuqest">请求接口</button>


  <div class="card"> 
    <button type="button" @click="count++">count is {{ count }}</button>
    <p>
      Edit
      <code>components/HelloWorld.vue</code> to test HMR
    </p>
  </div>
</template>

<style scoped>
.read-the-docs {
  color: #888;
}
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

完成~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gu2e9eJp-1672049178997)(assets/image-20221226180522-vmxwfw1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IxuJSFwQ-1672049178997)(assets/image-20221226180542-kx0rg6d.png)]

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/390344
推荐阅读
相关标签
  

闽ICP备14008679号