fetch API返回404时try/catch未生效的React请求问题
问题现象
接口请求URL通过模板字符串直接拼接用户输入内容,当用户输入不合法内容触发fetch API返回404状态码时,预期try/catch逻辑可捕获该异常,在控制台打印错误信息,页面展示「Error」提示,但实际运行时代码未进入catch分支,组件仍按正常流程渲染。
复现代码
import './cssdirect/App.css'; import Daily from './components/daily'; import {useState, useEffect} from 'react' import Loading from './Loading'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faHeart } from '@fortawesome/free-solid-svg-icons' function App() { const [loading, setLoading] = useState(true) const [city, setCity] = useState('Paris') const [weatherData, setWeather] = useState([]) const [error, setError] = useState(false) const getWeather = async() =>{ setLoading(true) setError(false) try{ const resp = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=imperial`) const pResp = await resp.json() setWeather(pResp) setLoading(false) } catch(err){ console.log("ERRRRROOOORRRR") setError(true) } } const handleSubmit = (e) =>{ e.preventDefault() console.log(city) getWeather() } useEffect(()=>{ getWeather() },[]) if(loading==true){ return <> <h1 className='title'>Monkey Wit Da Weather</h1> <Loading/> </> } else if(error===true){ return<> <h1>Error</h1> </> } else{ return<> <h1 className='title'>Monkey Wit Da Weather</h1> <Daily {...weatherData} setCity={setCity} city={city} getWeather={getWeather} handleSubmit={handleSubmit}/> </> } } export default App;
问题原因
fetch API 本身不会在HTTP响应状态码为4xx、5xx时自动抛出异常,只有网络中断、CORS跨域拦截、请求被浏览器阻止这类导致请求完全无法完成的场景,fetch才会返回rejected状态的Promise,被catch分支捕获。
当接口返回404状态码时,fetch实际上是正常返回了resolve状态的Response对象,仅对象上的ok属性为false、status属性值为404,因此代码不会进入catch分支,会继续执行后续的resp.json()逻辑,将接口返回的错误报文当成正常天气数据存入状态,最终组件走正常流程渲染。
另外原代码的catch分支没有重置loading状态,真的触发异常时页面会一直卡在加载态。
修复方案
- 拿到fetch返回的响应对象后,先通过
resp.ok属性判断响应状态是否正常,状态异常时手动抛出错误触发catch逻辑 - catch分支中补充
loading状态重置,避免页面永久停留在加载态
修复后的getWeather方法代码如下:
const getWeather = async() =>{ setLoading(true) setError(false) try{ const resp = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=imperial`) // 主动校验响应状态,非2xx状态时手动抛错 if (!resp.ok) { throw new Error(`Request failed with status: ${resp.status}`) } const pResp = await resp.json() setWeather(pResp) setLoading(false) } catch(err){ console.log("ERRRRROOOORRRR", err) setError(true) // 出错时重置加载状态 setLoading(false) } }
内容的提问来源于stack exchange,提问作者Het Patel
相关产品推荐
相关产品推荐

