惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
Google DeepMind News
Google DeepMind News
S
SegmentFault 最新的问题
Project Zero
Project Zero
D
DataBreaches.Net
I
InfoQ
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
The Register - Security
The Register - Security
Recorded Future
Recorded Future
Vercel News
Vercel News
博客园 - 司徒正美
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
I
Intezer
The Hacker News
The Hacker News
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
Help Net Security
Help Net Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Scott Helme
Scott Helme
T
Threatpost
爱范儿
爱范儿
N
Netflix TechBlog - Medium
D
Docker
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
K
Kaspersky official blog
H
Help Net Security
S
Secure Thoughts
T
Threat Research - Cisco Blogs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Security @ Cisco Blogs
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
G
Google Developers Blog
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
博客园 - 叶小钗
B
Blog
Google DeepMind News
Google DeepMind News
Recent Announcements
Recent Announcements
Simon Willison's Weblog
Simon Willison's Weblog
S
Securelist
P
Privacy International News Feed
Spread Privacy
Spread Privacy
The Last Watchdog
The Last Watchdog

博客园 - 一名程序媛呀

Anki插件开发必知必会:钩子函数与右键菜单定制 httpx 传参总报错?这次把 GET、POST、文件上传到响应处理的坑给你一次填平 从卡顿到丝滑:FastAPI 调用外部 API 的正确姿势(httpx 实战) 还在 XHR、Fetch 和 Axios 之间纠结?我踩过的坑,希望你一个都不用碰到 你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南 Uvicorn、Gunicorn 傻傻分不清?FastAPI 生产部署避坑指南 Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招! 刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API 别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了 掏出手机就能搭个 WebDAV 同步服务器?这操作有点香 别只盯着GitBook了!这个文档神器让你的笔记秒变网站 写爬虫时用了代理还被封?Python 代理的那些隐藏坑,我替你踩明白了 FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好 旧手机别扔!用 Termux 搭个私人云盘,比网盘香多了 你的FastAPI又在服务器上“跑不起来”了?来,今天咱把打包这件事彻底聊透 写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了 前端包管理咋选?我从npm叛逃到pnpm的血泪史(附避坑指南) FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示? 给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍 我用了FastApiAdmin后,连夜把踩过的坑都整理出来了 告别 Typora 后的新欢:我把所有笔记迁移到了 Obsidian 这个“第二大脑” 你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线 让 FastAPI Agent 思考不阻塞:手把手教你实现异步任务与后台处理方案 让FastAPI Agent真正记住你:聊聊会话记忆与持久化存储的落地实践 FastAPI Agent 函数调用实战:我让 AI 学会了“自己动手查天气“ 初探:用 FastAPI 搭建你的第一个 AI Agent 接口 FastAPI 少有人提的实用技巧:把 Depends 依赖提到路由层,代码少写60% FastAPI 生产环境静态文件完全指南:从 /favicon.ico 404 到 HSTS 混合内容,一次全根治 用了loguru我才明白,Python日志还能这么写 FastAPI 后台任务:BackgroundTasks 的使用场景与注意事项 FastAPI配置管理避坑指南:从硬编码到 .env 与 pydantic_settings 类,连路由用法都给你捋清楚 FastAPI 文件上传避坑全指南:分块存盘、类型校验与安全兜底 FastAPI + Pydantic 模型终极实战手册:从能跑就行到固若金汤,这些技巧你一定用得上 FastAPI + SQLAlchemy 2.0 通用CRUD操作手册 —— 从同步到异步,一次讲透 FastAPI订单防超卖实战:从数据库锁到Saga分布式事务,这一篇给你理清了 FastAPI 生产环境避坑指南:用 Alembic 管理数据库迁移,别再手动改表结构了! FastAPI服务半夜又挂了?先别急着重启,查查你的数据库连接池“池子”是不是漏了 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 Vue 3 组合式 API 香是香,但从Vue2迁移时你可别像我当初一样踩进这 3 个深坑里 我用fastapi-scaff搭了个项目,两天工期缩到两小时,老板以为我开挂了 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! FastAPI自动生成的API文档太丑?我花了一晚上把它改成了客户愿意付费的样子 告别手写 API 胶水代码:FastAPI 与 Vue 的“契约自动机” OpenAPI 实战 FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南” FastAPI + Celery 实战:异步任务里调用 Redis 和数据库的全解析,及生产级组织方案 FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了! FastAPI + Celery 实战:异步任务的坑与解法,我帮你踩了一遍 FastAPI子应用挂载:别再让root_path坑你一夜 FastAPI项目半夜报警吵醒你?聊聊告警这事儿怎么搞! 别再数据线了!用FastAPI 5分钟搭个局域网文件+剪贴板神器 FastAPI单元测试实战:别等上线被喷才后悔,TestClient用对了真香! FastAPI状态共享秘籍:别再让中间件、依赖和路由“各自为政”了! FastAPI实战:WebSocket vs Socket.IO,这回真给我整明白了! 从0到1,FastAPI + PostgreSQL + Tortoise ORM 实战避坑指南 FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀 FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透
聊聊 fetch 使用中我踩过的那些坑和正确打开方式
一名程序媛呀 · 2026-05-26 · via 博客园 - 一名程序媛呀

你是不是也遇到过这种情况:后端小哥信誓旦旦说接口没毛病,你自己用Postman试了也正常,可前端就是拿不到数据。

控制台一看,报的错牛头不对马嘴,什么“Failed to fetch”…… 更气人的是,明明写了 .catch(),错误却没抓住,眼睁睁看着页面白屏。

🎯 先说核心:fetch这玩意儿到底坑在哪儿

记得我刚开始写前端那会儿,一听原生的 fetch 比 XMLHttpRequest 好用,立马把项目里的 axios 全换了。结果项目一运行,报错信息差点没把我晃晕。

最核心的一个坑,也是90%新手会掉的——fetch 只把网络错误算作 reject,HTTP错误状态(比如404、500)它照样当成功处理

换句话说,哪怕服务器返回404,fetch的Promise仍然会走.then(),你要是不手动判断,后面一堆代码就拿着 undefined 开心地执行下去了。

这篇文章不跟你背文档,咱就像朋友聊天一样,把我这些年对 fetch 的理解、踩过的坑、还有现在一直用的“稳妥写法”,全倒给你。

📦 把fetch想象成点外卖

没错,我觉得 fetch 特别像点外卖。你下单(发请求),平台给你个订单号(Promise),外卖小哥会不会送到你家呢?不一定,可能商家自己取消订单了,但平台会告诉你“订单已生成”。

fetch 的 Response 对象就是这个“订单详情”。你得自己打开看看里面的 status (200?404?)和 ok 字段,不然外卖没送到你还给人打五星好评呢。

🛠️ 来,直接上我项目里在用的“安全代码”

咱们不废话,我先给你看一段可以直接复制的异步请求封装,然后再拆开说里头的门道。

async function request(url, options = {}) {
  try {
    const response = await fetch(url, options);
    // 重点1:先判断HTTP状态码
    if (!response.ok) {
      // 顺手把服务器返回的错误信息抛出去
      const errorBody = await response.text();
      throw new Error(`请求失败 ${response.status}: ${errorBody}`);
    }
    // 重点2:根据预期类型解析
    const contentType = response.headers.get("content-type");
    if (contentType && contentType.includes("application/json")) {
      return await response.json();
    }
    return await response.text();
  } catch (error) {
    // 重点3:在这儿统一处理上报和提示
    console.error("请求出错:", error);
    throw error; // 继续抛给调用方,别默默吞掉
  }
}

🎯 状态判断一定要用 response.ok

官方给了个现成属性 response.ok ,相当于 status 在 200-299 之间。千万别自己手写 if (status === 200) ,我早期吃过亏,碰到 201 Created 这种成功状态就直接卡死了。

📨 返回值的“一次性消费”特性

你可能会问,为啥上面我先把错误信息用 text() 读了,外面还能正常 json() ?因为我在抛错误前就已经消费掉了。

这引出 fetch 最容易翻车的点——Response 的 .json()、.text()、.blob() 都只能调一次。想既看原始文本又当 JSON 用?不行!要么先 clone() 一份,要么像我这样,只在确定是错误时才读一次。

再说个真实案例:有次我调试接口,想在控制台打印一下原始返回值,于是提前用 await response.json() 把数据拿出来了,接着返回给业务代码再用 .json() 一次,结果直接报错。调试了半天才发现是因为 body 流已经被读完了。

🔐 别忘了解析的那点小心思

经常有人问我:为啥后端明明返回了JSON,我的代码还是报错?

很多情况是后端在返回 401 或 403 时,虽然状态码不对,但 body 还是 JSON 格式的错误信息。如果你不先用 text() 看一眼直接 json(),很可能因为 content-type 不匹配而解析失败,结果连真正的错误都看不到。

我的习惯是,在非 ok 分支里一律先用 text() 拿到原始内容,既安全,又能把服务器给的提示原样显示出来,用户体验好不少。

🧵 最后啰嗦一句关于async/await

是不是以为写成 async/await 就万事大吉了?我可见过太多同学在 useEffect 里直接 async,或者 forEach 里用 await,结果数据像没睡醒一样迟迟不来。

如果你在循环里发请求,老老实实用 for...of 配合 await,或者用 Promise.all 并行处理。不然你可能会度过一个非常崩溃的调试下午。

拿我最开始翻车的代码举例吧,当时想批量拉取用户详情,写法大概长这样:

// ❌ 错误示范:forEach里用await,毛用没有
const userIds = [1, 2, 3];
const users = [];

userIds.forEach(async (id) => {
  const res = await fetch(`/api/user/${id}`);
  const data = await res.json();
  users.push(data);
});
// 这时候users大概率还是个空数组,因为forEach不等await
console.log(users); // 惊不惊喜?意不意外?

以为这样就能一个个按顺序拿到数据,但结果,天真了。forEach 的回调被标记为 async 后,并不会让外层的同步代码等它执行完,相当于你安排了三个人同时出门办事,还没等他们回来你就直接宣布“任务全部完成”了。

后来学乖了,改成了两种稳妥写法:

// ✅ 按顺序一个个来(适合有依赖的情况)
for (const id of userIds) {
  const res = await fetch(`/api/user/${id}`);
  const data = await res.json();
  users.push(data);
}

// ✅ 或者不关心顺序,全扔出去并行跑
const promises = userIds.map(id => 
  fetch(`/api/user/${id}`).then(res => res.json())
);
const users = await Promise.all(promises);

记住了,只要在循环里用 await,老老实实放弃 forEach,拥抱 for...of,或者直接用 map + Promise.all。这个坑我替你踩过了,别再往里掉啦!!!


好啦,今天掏心窝子的分享就到这儿。如果你在 fetch 上还躺过其他奇奇怪怪的坑,或者对我的封装有啥改进建议,特别欢迎给我留言。要是觉得这篇唠嗑能帮你省几根头发,动动手指点个“赞”,别让它在收藏夹吃灰呀~ 🌟