在前端开发中,网络请求是非常常见的操作。企业级项目都要求对网络请求的样板代码进一步封装后才能使用,因为封装之后的代码使用起来更简洁,更方便。同时封装之后的代码可屏蔽具体的依赖库,方便替换。可屏蔽URL,可统一拦截携带token,可统一响应消息等
Fetch API 的基本用法
fetch('https://jsonplaceholder.typicode.com/todos')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
在上面的代码中,我们使用了 fetch 函数来发送一个 GET 请求。fetch 函数返回一个 Promise 对象,我们可以使用 .then 方法来处理响应。在这个例子中,我们首先把响应转换为 JSON 格式,然后打印出数据。如果发生错误,我们使用 .catch 方法来捕获并处理错误。
// fetch发送post请求,携带token的用法
fetch(url, {
method: "POST",
headers: {
"Accept": "*/*",
"Content-Type": "application/json;charset=utf-8",
"X-Custom-Token": localStorage.getItem("access-token")
},
body:JSON.stringify(data)
}).then(res => res.json()).then(data => {
console.log(data)
}).catch(error => {
console.log(error.msg)
})
}
在上面的代码中,我们使用了 fetch 函数来发送一个 POST 请求。POST请求通常携带的参数都是Json类型,所以必须在请求头设置 “Content-Type”: “application/json;charset=utf-8”,意思是说,这次请求的数据内容类型为JSON,且是“utf-8”编码,支持中文。为了保证能够顺利的访问到对应的接口,通常还要携带“token”凭证,以确保我们身份的合法性,这样我们在请求头中,就需要添加类似代码 “X-Custom-Token”: localStorage.getItem(“access-token”) ,X-Custom-Token 是前后端双方约定的请求头“字段”,用来在请求头众多数据中,快速找到token用的。
以上样板代码,如果没有封装,那么预示着项目中每个网络请求的地方,都要写这么啰嗦的样板代码,那是难以维护的,且相当恐怖的,所以封装简化势在必行。
Fetch 的二次封装
由于fetch函数,没有提供向axios 那样的拦截器功能,所以对Fetch函数的封装,就是用自定义函数request简单的包一下。
注意:在src目录下 创建utils目录,然后创建request.js或request.ts 文件
//接口地址 也可以通过环境文件注入进来
const baseURL = 'http://localhost:3000'
/**
* 将对象转成 a=1&b=2的形式
* @param obj 对象
*/
function obj2String(obj, arr = [], idx = 0) {
for (let item in obj) {
arr[idx++] = [item, obj[item]]
}
return new URLSearchParams(arr).toString()
}
/**
* 请求封装
* @param url
* @param params
* @param method
* @returns {Promise<unknown>}
*/
export default (url, params, method = 'GET') => {
return new Promise((resolve, reject) => {
let initObj = {
method: method,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Custom-Token': localStorage.getItem('access-token')
}
}
if (method.toUpperCase() === 'GET') {
const searchStr = obj2String(params)
// GET请求,拼接url
if(searchStr){
url += '?' + searchStr
}
} else {
//POST或PUT请求 增加 application/json
Object.assign(initObj.headers, { 'Content-Type': 'application/json;charset=utf-8' })
initObj = {
...initObj,
body: JSON.stringify(params)
}
}
console.log(initObj)
fetch(baseURL + url, initObj)
.then((res) => res.json())
.then((res) => {
//res.code 必须和后端约定好 统一响应格式 包含{code:"",message:"",data:""}
switch (res.code) {
case 200:
console.log('成功了')
resolve(res.data)
break
default: {
// reject(res.message) //可选
alert(res.message) //换成ElmentUI 弹框更好
}
}
})
.catch((err) => {
console.log(err)
alert('网络请求异常') //换成ElmentUI 弹框更好
})
})
}
封装之后的使用案例
//首先要导入封装之后的文件
import request from '@/utils/request'
/**
* 获取分类
*/
const getGroupsList = () => {
//网络请求 ,不需要写ip地址 因为已经在封装体内置了
request('/groups').then((data: any) => {
console.log('结果', data)
})
}
/**
* 更新文章
*/
const updateArticle = () => {
//网络请求 ,不需要写ip地址 因为已经在封装体内置了
request(
'/update',
{
id: '编号',
content: '内容',
author: '作者'
},
'POST'
).then((data: any) => {
console.log('结果', data)
})
}
其他网络请求框架(Axios)封装
Axios的官网:https://www.axios-http.cn/
说明:一样的封装思路,相比Fetch函数 Axios的封装就比较优雅,因为其本身提供了拦截器功能 ,可以很方便的进行请求拦截,携带Token等。
1、安装axios
$ npm install axios
2、依然在utils目录下,依然创建request.js文件
import axios from "axios";
import {ElNotification} from 'element-plus'
import {API_HOST,API_BASE_URL} from "@/config/index.js";
import { ElLoading } from 'element-plus'
// 创建Axios实例
const service = axios.create({
baseURL:API_HOST+API_BASE_URL, // API基础地址
timeout: 5000, // 请求超时时间
});
/**
* 加载条
* @type {{show(*): void, close(): void}}
*/
const loading = (() => {
let loadingInstance = null;
return {
show(text) {
loadingInstance = ElLoading.service({
lock: true,
text,
background: "rgba(0, 0, 0, 0.6)",
});
},
close() {
loadingInstance.close();
},
};
})();
// 请求拦截器
service.interceptors.request.use(
config => {
loading.show("加载中");
// 在发送请求之前做些什么,例如添加token
config.headers['X-Custom-Token'] = sessionStorage.getItem('access-token');
return config;
},
error => {
// 对请求错误做点什么
console.log(error);
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
response => {
loading.close();
const result = response.data
console.log('请求接口的结果:',result)
if (result.code!==200) {
ElNotification({
title: 'Warning',
message: result.message||'请求结果不合理',
type: 'warning',
})
return Promise.reject(result.data)
}
return result.data; //取出最终数据
},
error => {
loading.close();
ElNotification({
title: 'Error',
message: error.message||'接口请求出现错误',
type: 'error',
})
return Promise.reject(error.response.data);
}
);
export default service;
最后
如何跟服务端拦截器配合,请参考sprintboot拦截器相关篇章