关于 JS HTTP request client library,现阶段我推荐 wretch。
我的技术栈变化:wretch <- fetch <- node-fetch <- axios
我推荐 wretch 的理由:
- 适当的封装,不需要编写大量业务无关的 boilerplate code
- 统一实现的请求格式,如:请求 application/json, multipart/form-data, application/x-www-form-urlencoded
- 开箱即用的高级功能,如:retry, delay, abort 等等
技术栈变化简述
- axios: ES 提出 fetch() API 前,只记得 axios,XMLHttpRequest 我已经不记得了。
- node-fetch: ES 提出 fetch() API 后,但是 Node.js 没有实现出来
- fetch: Node.js 18 发布 fetch() API 后
- wretch: 写 fetch() 太疲劳了,寻找简单的工具
1. 什么是“不需要编写大量业务无关的 boilerplate code”
fetch 需要手动配置 http methods, headers and body
fetch("endpoint", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ "hello": "world" })
}).then(response => /* ... */)
fetch() 需要手动处理 status,堆叠 if-else
// 😮💨
fetch("anything")
.then(response => {
if(!response.ok) {
if(response.status === 404) throw new Error("Not found")
else if(response.status === 401) throw new Error("Unauthorized")
else if(response.status === 418) throw new Error("I'm a teapot !")
else throw new Error("Other error")
}
else // ...
})
.then(data => /* ... */)
.catch(error => { /* ... */ })
而这些都是业务无关的代码,仅仅是为了配合 fetch() API 的使用。
wretch 封装了这些业务无关的代码,让你专注于业务逻辑。
wretch("endpoint")
.post({ "hello": "world" })
.notFound(error => { /* ... */ })
.unauthorized(error => { /* ... */ })
.error(418, error => { /* ... */ })
.res(response => /* ... */)
.catch(error => { /* uncaught errors */ })
2. 什么是“统一实现的请求格式”
wretch 无论是发送 json 或 form-data 亦或 x-www-form-urlencoded 都可以直接调用 JS object 对象。
So Sweet 🥰
const form = { a: 1, b: { c: 2 } };
// 📤 application/json
wretch("...").post(form)
// 📤 multipart/form-data
import FormDataAddon from "wretch/addons/formData"
wretch("...").addon(FormDataAddon).formData(form).post();
// 📤 application/x-www-form-urlencoded
import FormUrlAddon from "wretch/addons/formUrl"
wretch("...").addon(FormUrlAddon).formUrl(form).post();
fetch()
- 发送 json 需要
JSON.stringify();
- 发送 form-data 需要构建
new FormData();
- 发送 x-www-form-urlencoded 需要
new URLSearchParams();
Why? 😵💫
const data = { a: 1, b: { c: 2 } }
// 📤 application/json
fetch("endpoint", {
method: "POST",
headers: { "Content-Type": "application/json" }, // 1️⃣
body: JSON.stringify(data) // 2️⃣
}).then(response => /* ... */)
// 📤 multipar/form-data
const formData = new FormData(); // 1️⃣
formData.append('a', 1);
formData.append('b', JSON.stringify({ c: 2})); // 2️⃣
fetch('https://example.com/submit', {
method: 'POST',
body: formData
}).then(response => /* ... */)
// 📤 application/x-www-form-urlencoded
const data = new URLSearchParams(); // 1️⃣
data.append('a', 1);
data.append('b', JSON.stringify({ c: 2})); // 2️⃣
fetch('https://example.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded' // 3️⃣
},
body: data.toString()() // 4️⃣
}).then(response => /* ... */)
3. wretch 提供的“开箱即用的高级功能”
wretch 实现 retry 功能 So Easy
import { retry } from "wretch/middlewares"
const w = wretch().middlewares([retry()])
fetch() 实现 retry 功能,So Hard
// generated by ChatGPT
function fetchWithRetry(url, options = {}, retries = 3, delay = 1000) {
return fetch(url, options)
.then(response => {
if (!response.ok) {
if (retries > 0) {
console.log(`Retrying... ${retries} attempts left.`);
return new Promise((resolve) => {
setTimeout(() => resolve(fetchWithRetry(url, options, retries - 1, delay)), delay);
});
} else {
throw new Error('Max retries reached, fetch failed.');
}
}
return response.json(); // 处理返回的响应体(假设是 JSON 格式)
})
.catch(error => {
if (retries > 0) {
console.log(`Retrying due to error: ${error.message}, ${retries} attempts left.`);
return new Promise((resolve) => {
setTimeout(() => resolve(fetchWithRetry(url, options, retries - 1, delay)), delay);
});
} else {
throw new Error('Max retries reached, fetch failed: ' + error.message);
}
});
}
// 调用
fetchWithRetry('https://example.com/api', { method: 'GET' })
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
更不用提 delay abort 等高级功能了。
我发现的 wretch 的一点点不足
- progress 特性仅支持 download https://github.com/elbywan/wretch/issues/225#issuecomment-2105633417
- 没有开箱即用 Stream handler
refs