赞
踩
service
目录——存放接口相关文件创建目录结构如下
controller
:前端直接与后端通信的层dto
:后端传给前端的vo
:前端传给后端的request
:封装请求接口的使用 NPM:
npm install axios
使用 Yarn:
yarn axios
使用 PNPM:
pnpm i aixos
安装成功
在service
目录下创建instance.ts
文件,创建一个axios请求类
export default class AxiosInstance {
}
创建类型
/** 请求的相关类型 */ 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; } }
定义属性
instance: AxiosInstance;
backendConfig: Service.BackendResultConfig;
定义构造方法
/** * * @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); }
搭建整体架构
/** 设置请求拦截器 */
setInterceptor(){
// 拦截请求
this.instance.interceptors.request.use(
)
// 拦截响应
this.instance.interceptors.response.use(
);
}
处理正常请求
在拦截请求处可添加对
token
或者数据的转换
async (config:AxiosRequestConfig<any>) => {
const handleConfig = {...config};
if (handleConfig.headers) {
// 数据转换
// 可在此处添加设置token
}
},
添加基本配置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];
统一处理请求异常
(axiosError: AxiosError) => {
console.log('axiosError',axiosError)
}
// 拦截响应 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]; } } } );
添加请求接口类型
type RequestMethod = 'get' | 'post' | 'put' | 'delete';
添加请求参数类型
interface RequestParam {
url: string;
method?: RequestMethod;
data?: any;
axiosConfig?: AxiosRequestConfig;
}
创建请求
/** * 创建请求 * @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; }
添加请求方法(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 };
创建环境配置
在根目录下创建.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; }
设置对应的类型
在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; }
创建接口
import { getServiceEnvConfig } from '~/.env-config';
import { createRequest } from "./request";
const { url } = getServiceEnvConfig(import.meta.env);
export const web = createRequest({baseURL: url})
创建user子目录
import { web } from "../../request";
export function login(){
return web.post<String>('/user/login')
}
统一导出
export * as userRequest from './user/request'
使用
<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>
完成~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。