智能助手 

外观
在前端开发中,封装请求方法是一种常见的实践,主要是为了提高代码的可维护性、可读性和复用性。以下是一些具体的原因:
代码复用:
简化调用:
统一错误处理:
方便的请求配置管理:
增强可测试性:
支持不同的请求库:
axios
换到fetch
),只需要在封装层进行修改,而不需要更改所有使用请求的地方。添加中间件或拦截器:
通过封装请求方法,开发者能够更好地管理和控制应用程序中的网络请求,提升代码的质量和维护效率。
提供的能力: 设置默认配置、请求和响应拦截器、常用的 HTTP 方法(GET、POST、PUT、DELETE)以及文件下载功能
。
import axios from "axios";
class HttpService {
/**
* @description 默认axios配置
* @type {Object}
* @property {string} baseURL 请求基础路径
* @property {number} timeout 请求超时时间
*/
static defaultAxiosOptions = {
baseURL: "",
timeout: 1000 * 60,
};
/**
* @param {*} axiosOption axios相关的配置项
*/
constructor(axiosOptions = {}) {
this.axiosOptions = { ...HttpService.defaultAxiosOptions, ...axiosOptions };
this.axiosInstance = axios.create(this.axiosOptions);
this.#initInterceptors();
}
#initInterceptors() {
this.axiosInstance.interceptors.request.use(
(config) => {
return config;
},
(error) => {
return Promise.reject(error);
}
);
this.axiosInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
return Promise.reject(error);
}
);
}
/**
* @description 请求拦截器
* @param {Function} onFulfilled 请求成功回调
* @param {Function} onRejected 请求失败回调
*/
setRequestInterceptor(onFulfilled, onRejected) {
this.axiosInstance.interceptors.request.use(onFulfilled, onRejected);
}
/**
* @description 响应拦截器
* @param {Function} onFulfilled 响应成功回调
* @param {Function} onRejected 响应失败回调
*/
setResponseInterceptor(onFulfilled, onRejected) {
this.axiosInstance.interceptors.response.use(onFulfilled, onRejected);
}
/**
*
* @param {Object } config
* @returns {Function}
*/
async request(config) {
const executeRequest = async () => {
try {
const response = await this.axiosInstance({ ...config });
loadingCallback?.onEnd?.();
return response;
} catch (error) {
loadingCallback?.onEnd?.();
throw error;
}
};
return executeRequest();
}
get(url, params, config = {}) {
return this.request({ url, method: "GET", params, ...config });
}
post(url, data, config = {}) {
return this.request({ url, method: "POST", data, ...config });
}
put(url, data, config = {}) {
return this.request({ url, method: "PUT", data, ...config });
}
delete(url, config = {}) {
return this.request({ url, method: "DELETE", ...config });
}
// 下载文件
async downloadFile(url, method, params, config = {}) {
let response;
if (method === "GET") {
response = await this.get(url, params, {
responseType: "blob",
...config,
});
}
if (method === "POST") {
response = await this.post(url, params, {
responseType: "blob",
...config,
});
}
let fileName = "downloaded_file";
const contentDisposition = response.headers["content-disposition"];
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename="?(.+)"?/);
if (fileNameMatch && fileNameMatch.length === 2) {
fileName = fileNameMatch[1];
}
}
const urlBlob = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = urlBlob;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
link.remove();
}
// GET方式下载文件
async downloadFileGet(url, params, config = {}) {
return this.downloadFile(url, "GET", params, config);
}
// POST方式下载文件
async downloadFilePost(url, data, config = {}) {
return this.downloadFile(url, "POST", data, config);
}
}
export default HttpService;
以下是使用 HttpService
类实现的请求
import HttpService from "./HttpService";
// 整体设置axios配置
const httpService = new HttpService({
baseURL: "https://api.example.com",
timeout: 1000 * 60,
});
httpService.setResponseInterceptor(
(response) => {
return response.data;
},
(error) => {
return Promise.reject(error);
}
);
// get
async function fetchUserData(userId) {
try {
const response = await httpService.get(`/users/${userId}`);
console.log("User Data:", response.data);
} catch (error) {
console.error("Error fetching user data:", error);
}
}
fetchUserData(1);
// post
async function loginUser(credentials) {
try {
// 请求内部单个设置axios配置
const response = await httpService.post("/login", credentials, {
headers: { custom: "example" },
});
console.log("Login Response:", response.data);
} catch (error) {
console.error("Error logging in:", error);
}
}
const credentials = {
username: "exampleUser",
password: "examplePassword",
};
loginUser(credentials);
// downloadFileGet下载文件
async function downloadUserReport(userId) {
try {
await httpService.downloadFileGet(`/users/${userId}/report`, null, {
responseType: "blob",
});
console.log("File downloaded successfully");
} catch (error) {
console.error("Error downloading file:", error);
}
}
downloadUserReport(1);
提供的能力: 请求重试、取消重复请求、请求前后回调
import axios from "axios";
class HttpService {
/**
* @description 默认axios配置
* @type {Object}
* @property {string} baseURL 请求基础路径
* @property {number} timeout 请求超时时间
*/
static defaultAxiosOptions = {
baseURL: "",
timeout: 1000 * 60,
};
/**
* @description 默认自定义配置
* @type {Object}
* @property {number} retry 请求重试次数
* @property {number} retryDelay 请求重试间隔 ,使用指数退避算法
* @property {Object} loadingCallback 请求前后回调
* @property {boolean} cancelDuplicate 是否取消重复请求
* @property {number} cancelDuplicateDelay 请求重复间隔
*/
static defaultCustomOptions = {
retry: 0,
retryDelay: 200,
loadingCallback: {
onStart: () => {},
onEnd: () => {},
},
cancelDuplicate: false,
cancelDuplicateDelay: 500,
};
/**
*
* @param {*} axiosOption axios相关的配置项
* @param {*} customOption 自定义的配置项:包含请求重试、请求拒绝、请求前后回调
*/
constructor(axiosOptions = {}, customOptions = {}) {
this.axiosOptions = { ...HttpService.defaultAxiosOptions, ...axiosOptions };
this.customOptions = {
...HttpService.defaultCustomOptions,
...customOptions,
};
this.axiosInstance = axios.create(this.axiosOptions);
this.pendingRequests = new Map();
this.#initInterceptors();
}
#initInterceptors() {
this.axiosInstance.interceptors.request.use(
(config) => {
return config;
},
(error) => {
return Promise.reject(error);
}
);
this.axiosInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
return Promise.reject(error);
}
);
}
/**
* @description 请求拦截器
* @param {Function} onFulfilled 请求成功回调
* @param {Function} onRejected 请求失败回调
*/
setRequestInterceptor(onFulfilled, onRejected) {
this.axiosInstance.interceptors.request.use(onFulfilled, onRejected);
}
/**
* @description 响应拦截器
* @param {Function} onFulfilled 响应成功回调
* @param {Function} onRejected 响应失败回调
*/
setResponseInterceptor(onFulfilled, onRejected) {
this.axiosInstance.interceptors.response.use(onFulfilled, onRejected);
}
/**
*
* @param {Object } config
* @param {Object} customOptions
* @returns {Function}
*/
async request(config, customOptions = {}) {
const finalCustomOptions = { ...this.customOptions, ...customOptions };
const {
retry,
retryDelay,
loadingCallback,
cancelDuplicate,
cancelDuplicateDelay,
} = finalCustomOptions;
const requestKey =
config.method + config.url + JSON.stringify(config.params);
if (cancelDuplicate && this.pendingRequests.has(requestKey)) {
const controller = this.pendingRequests.get(requestKey);
controller.abort();
this.pendingRequests.delete(requestKey);
await this.#delay(cancelDuplicateDelay);
}
const controller = new AbortController();
this.pendingRequests.set(requestKey, controller);
let retries = 0;
const executeRequest = async () => {
loadingCallback?.onStart?.();
try {
const response = await this.axiosInstance({
...config,
signal: controller.signal,
});
this.pendingRequests.delete(requestKey);
loadingCallback?.onEnd?.();
return response;
} catch (error) {
if (axios.isCancel(error)) {
throw error;
} else if (retry && retries < retry) {
retries++;
await this.#delay(retryDelay * retries);
return executeRequest();
} else {
this.pendingRequests.delete(requestKey);
loadingCallback?.onEnd?.();
throw error;
}
}
};
return executeRequest();
}
#delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
get(url, params, config = {}, customOptions = {}) {
return this.request(
{ url, method: "GET", params, ...config },
customOptions
);
}
post(url, data, config = {}, customOptions = {}) {
return this.request(
{ url, method: "POST", data, ...config },
customOptions
);
}
put(url, data, config = {}, customOptions = {}) {
return this.request({ url, method: "PUT", data, ...config }, customOptions);
}
delete(url, config = {}, customOptions = {}) {
return this.request({ url, method: "DELETE", ...config }, customOptions);
}
// 下载文件
async downloadFile(url, method, params, config = {}, customOptions = {}) {
let response;
if (method === "GET") {
response = await this.get(
url,
params,
{ responseType: "blob", ...config },
customOptions
);
}
if (method === "POST") {
response = await this.post(
url,
params,
{ responseType: "blob", ...config },
customOptions
);
}
let fileName = "downloaded_file";
const contentDisposition = response.headers["content-disposition"];
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename="?(.+)"?/);
if (fileNameMatch && fileNameMatch.length === 2) {
fileName = fileNameMatch[1];
}
}
const urlBlob = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = urlBlob;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
link.remove();
}
// GET方式下载文件
async downloadFileGet(url, params, config = {}, customOptions = {}) {
return this.downloadFile(url, "GET", params, config, customOptions);
}
// POST方式下载文件
async downloadFilePost(url, data, config = {}, customOptions = {}) {
return this.downloadFile(url, "POST", data, config, customOptions);
}
}
export default HttpService;
该配置可以实现最多三次的请求失败重试, 重试间隔按照指数退避原则进行;同时提供在请求发起前的 hook 和请求结束后的 hook;同时对所有请求做 500ms 以内的防抖,防止短时间内的重复请求
import HttpService from "./HttpService";
// 整体设置axios配置
const httpService = new HttpService(
{
baseURL: "https://api.example.com",
timeout: 1000 * 60,
},
{
retry: 3,
retryDelay: 200,
loadingCallback: {
onStart: () => {
console.log("onStart");
},
onEnd: () => {
console.log("onEnd");
},
},
cancelDuplicate: true,
cancelDuplicateDelay: 500,
}
);
httpService.setResponseInterceptor(
(response) => {
return response.data;
},
(error) => {
return Promise.reject(error);
}
);
// get
async function fetchUserData(userId) {
try {
const response = await httpService.get(`/users/${userId}`);
console.log("User Data:", response.data);
} catch (error) {
console.error("Error fetching user data:", error);
}
}
fetchUserData(1);
// post
async function loginUser(credentials) {
try {
// 请求内部单个设置axios配置
const response = await httpService.post("/login", credentials, {
headers: { custom: "example" },
});
console.log("Login Response:", response.data);
} catch (error) {
console.error("Error logging in:", error);
}
}
const credentials = {
username: "exampleUser",
password: "examplePassword",
};
loginUser(credentials);
// downloadFileGet下载文件
async function downloadUserReport(userId) {
try {
await httpService.downloadFileGet(`/users/${userId}/report`, null, {
responseType: "blob",
});
console.log("File downloaded successfully");
} catch (error) {
console.error("Error downloading file:", error);
}
}
downloadUserReport(1);
该示例实现对特定url
请求的高配置功能。
import HttpService from "./HttpService";
// 整体设置axios配置
const httpService = new HttpService({
baseURL: "https://api.example.com",
timeout: 1000 * 60,
});
httpService.setResponseInterceptor(
(response) => {
return response.data;
},
(error) => {
return Promise.reject(error);
}
);
// get
async function fetchUserData(userId) {
try {
const response = await httpService.get(`/users/${userId}`);
console.log("User Data:", response.data);
} catch (error) {
console.error("Error fetching user data:", error);
}
}
fetchUserData(1);
// post
async function loginUser(credentials) {
try {
// 请求内部单个设置axios配置
const response = await httpService.post(
"/login",
credentials,
{
headers: { custom: "example" },
},
{
retry: 3,
retryDelay: 200,
loadingCallback: {
onStart: () => {
console.log("onStart");
},
onEnd: () => {
console.log("onEnd");
},
},
cancelDuplicate: true,
cancelDuplicateDelay: 500,
}
);
console.log("Login Response:", response.data);
} catch (error) {
console.error("Error logging in:", error);
}
}
const credentials = {
username: "exampleUser",
password: "examplePassword",
};
loginUser(credentials);
// downloadFileGet下载文件
async function downloadUserReport(userId) {
try {
await httpService.downloadFileGet(`/users/${userId}/report`, null, {
responseType: "blob",
});
console.log("File downloaded successfully");
} catch (error) {
console.error("Error downloading file:", error);
}
}
downloadUserReport(1);
版权归属:tuyongtao1