为什么用 fetch 发送 PATCH 请求返回 404 / 500,代码却没有进入 catch?正确的错误处理应该怎么写?
这是 fetch 最容易踩的坑之一。
原因:fetch 只在「网络层失败」时才 reject
fetch 的设计是:只有在网络请求本身失败(断网、DNS 解析失败、CORS 被拦截、请求被 abort)时才会 reject 并进入 catch。
而 HTTP 状态码 404 / 500 属于「请求成功到达服务器并拿到了响应」,只是响应状态码表示业务失败——这种情况 fetch 认为请求是「成功完成」的,会正常 resolve,Promise 进入 then,而不会进入 catch。
// ❌ 错误写法:以为 404/500 会进 catch
fetch('/api/user', { method: 'PATCH', body: JSON.stringify(data) })
.then(res => {
// 404/500 也会走到这里!
return res.json()
})
.catch(err => {
// 只有网络错误才进这里
})正确做法:手动检查响应状态
fetch('/api/user', { method: 'PATCH', body: JSON.stringify(data) })
.then(res => {
if (!res.ok) {
// res.ok 为 false 表示状态码 >= 400
throw new Error('请求失败:' + res.status + ' ' + res.statusText)
}
return res.json()
})
.then(data => { /* 业务成功 */ })
.catch(err => { /* 网络错误 + 主动抛出的 HTTP 错误都到这里 */ })
// 或更推荐的 async/await 写法
async function patchUser(data) {
const res = await fetch('/api/user', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!res.ok) {
throw new Error('HTTP ' + res.status)
}
return res.json()
}常见误区
- 误区:axios 会对 4xx/5xx 自动 reject,于是把 axios 习惯套到 fetch 上。
- 注意:axios 在 4xx/5xx 时确实会 reject;但
fetch不会,二者行为不同。 - 可用
res.status >= 400判断,但res.ok语义更清晰(等价于200 <= status < 300)。